Compare commits

...
369 Commits
Author SHA1 Message Date
Lucas.Xu 565485265b chore: prerelease 0.1.2 (#2134) 2023-03-29 15:14:28 +08:00
Lucas.Xu f6f615a54c chore: release 0.1.2 (#2129) 2023-03-28 18:37:08 +08:00
Nathan.fooo 17d88f2ad3 fix: update calendar selected range (#2123) 2023-03-28 14:34:00 +08:00
Nathan.fooo 8f66246e25 fix: modify same database by duplicate view (#2121) 2023-03-28 13:00:20 +08:00
Lucas.Xuandvedon 063ec186bb Rename project and upgrade windows project (#2072)
* chore: rename product name

* chore: upgrade windows project to latest template

---------

Co-authored-by: vedon <vedon.fu@gmail.com>
2023-03-22 13:12:16 +08:00
Annie ec444d4a00 chore: add release changes 2023-03-21 20:22:16 +08:00
Lucas.Xu 95475cb870 Fix some known issue for release/0.1.1 (#2061)
* fix: only show discard dialog when prompt text is not empty

* fix: auto completion node widget text overflow

* chore: update bundle name and copyright
2023-03-21 15:33:19 +08:00
Lucas.Xu 0520855925 chore: update languages file (#2034) 2023-03-20 18:43:31 +08:00
Lucas.Xu 61704f6d4a Fix release v0.1.1 known issues. (#2025)
* feat: optimize the smart edit user-experience

* chore: update language files

* feat: show discard dialog when users tap blank are that out of ai plugins

* fix: toolbar_service test fail
2023-03-20 13:49:20 +08:00
Nathan.fooo b85990e453 feat: migration database (#2009)
* feat: migration database

* ci: fix tauri ci

* feat: migrate database view

* ci: fix ci
2023-03-18 07:21:28 +08:00
Lucas.Xu b0d525a48f Merge pull request #2004 from AppFlowy-IO/chore/0.1.1
release 0.1.1
2023-03-17 13:54:56 +08:00
Nathan.fooo bf048ffc0d fix: paser workspce pb (#2005) 2023-03-17 13:47:27 +08:00
Lucas.Xu 1f44d4de33 fix(test): test replaceTexts, textNodes.length > texts.length 2023-03-16 21:14:45 +08:00
Lucas.Xu 653f88033e feat: improve the smart edit user-experience 2023-03-16 18:40:20 +08:00
Lucas.Xu 0c88c7ce2f fix: windows package build error 2023-03-16 15:24:45 +08:00
Lucas.Xu f38688b17e chore: update release version to 0.1.1 2023-03-16 14:26:22 +08:00
Lucas.Xu 1a3701feaf feat: #2003 Entering an OpenAI key should auto-save 2023-03-16 14:26:05 +08:00
Lucas.Xu 1b86587875 chore: update language files 2023-03-16 14:25:39 +08:00
Lucas.Xu 120db6805f feat: open gitbook openAI link when tapping learn more button (#2002) 2023-03-16 12:35:20 +08:00
Lucas.Xu 6b14983f2b fix: #1997 Exception trhown after inserting '/' slash in middle of text (#1998) 2023-03-15 17:48:56 +08:00
Lucas.Xu 9a4cde07b6 Merge pull request #1991 from AppFlowy-IO/fix_0_1_1
Fix release/0.1.1 known issues.
2023-03-14 17:17:47 +08:00
Lucas.Xu d6d591f359 fix: select all shouldn't include the cover plugin 2023-03-14 16:41:37 +08:00
Lucas.Xu 0570615414 fix: #1975 Set a height limit for the AI prompt window where it displays the results 2023-03-14 15:57:26 +08:00
Lucas.Xu 609bf2a4ce fix: #1968 UI inconsistency 2023-03-14 11:44:02 +08:00
Lucas.Xu 75231e606c fix: flutter analyze 2023-03-13 22:28:56 +08:00
Lucas.Xu dd9c3dd2a3 fix: #1967 the summarize feature generates duplicate answers 2023-03-13 21:35:36 +08:00
Lucas.Xu 4821d7ea65 fix: #1966 the loading icon too close to the edge 2023-03-13 17:53:22 +08:00
Lucas.Xu 874d1e86c2 fix: #1964 Text on the UI 2023-03-13 17:44:59 +08:00
Lucas.Xu b7ec7ce32e fix: #1962 Disable but still show the AI assistants icon in the toolbar menu when an OpenAI key is not provided 2023-03-13 17:28:10 +08:00
Lucas.Xu 0141538be8 fix: #1973 Using the mouse to highlight text very easy to miss the first letter 2023-03-13 14:35:11 +08:00
Lucas.Xu dcf48ceb50 fix: #1976 Adding a cover image via upload doesn't work 2023-03-13 14:30:52 +08:00
Yijing HuangandLucas.Xu 972ef2149c fix(appflowy_flutter): fix cover image overflow #1916 (#1952)
* fix(appflowy_flutter): fix cover image overflow #1916

* fix(appflowy_flutter): use OverflowBox to fix #1916

* chore: fix misspelling

* fix: prevent the image being overstretched

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2023-03-11 21:15:34 +08:00
668e1196d1 chore: config tauri web driver test (#1947)
* chore: setup tauri test

* chore: update test

* chore: update test

* chore: update test

* chore: update test

* refactor: test folder location

* chore: remove deps

* ci: fix build

---------

Co-authored-by: appflowy <annie@appflowy.io>
Co-authored-by: nathan <nathan@appflowy.io>
2023-03-11 13:07:31 +08:00
Richard Shiue 688d55e00f fix: #1942 utc timestamp being parsed as local time (#1953) 2023-03-10 17:33:25 +08:00
Lucas.Xu 40c23b7c7d Merge pull request #1951 from hyj1204/fix/double_click_title_issue_1324
fix(appflowy_flutter): fix double click title issue #1324
2023-03-10 10:25:46 +07:00
Nathan.fooo 5b4043b805 fix: SelectOptionCellBloc registeration error (#1948) 2023-03-10 09:40:19 +08:00
Yijing Huang 2368f5dc4a fix(appflowy_flutter): fix double click title issue #1324
double click the title to select all the text on it
2023-03-10 00:24:31 +08:00
GouravShDev 0dac41b114 chore: add node tests (#1943) 2023-03-09 14:33:12 +08:00
Lucas.Xu b89c69f294 fix: #1778 (#1946)
* fix: [FR] The text formatting toolbar should appear after the selection #1778

* chore: format code
2023-03-09 14:32:49 +08:00
Richard Shiue 77ff2e987a chore: include time per cell (#1901)
* style: autoformat

* chore: add include_time to cell data

* chore: remove include_time from date field type options

* chore: fix tests

* chore: custom deserializer for date cell data

* chore: add more tests

* chore: simplify date calculation logic

* chore: move include time to per-cell setting in UI

* test: add another text str test

* chore: adapt changes from upstream
2023-03-09 14:25:33 +08:00
Lucas.XuandYijing Huang 5e8f6a53a0 relicense appflowy editor (#1938)
* revert:"fix: remove keyword when click selection menu item"

This reverts commit 5782dec45c.

* revert(appflowy_editor):revert "feat: double asterisks/underscores to bold text"

This reverts commit c0964fad5d.

* revert(appflowy_editor):revert "fix: workaround infinity formatting"

This reverts commit 6a902a2b21.
The Appflowy folder under the frontend had been removed before reverting.

* chore(appflow_editor):update test variable after reverting

* chore(appflowy_editor): comment out the test for reverting

* chore(appflowy_editor): update variable type after reverting

* chore(appflowy_editor): remove unused import after reverting

* feat(appflowy_editor): double asterisk to bold text

* test(appflowy_editor): test double asterisk to bold text

* fix(appflowy_editor): delete slash after a selection menu item is selected

* test(appflowy_editor): test selection menu widget after clicking

* feat(appflowy_editor):  double asterisk to bold text and remove slash after clicking selection menu item   (#1935)

* feat(appflowy_editor): double asterisk to bold text

* test(appflowy_editor): test double asterisk to bold text

* fix(appflowy_editor): delete slash after a selection menu item is selected

* test(appflowy_editor): test selection menu widget after clicking

* feat(appflowy_editor): double underscore to bold text

* test(appflowy_editor): test double underscore to bold text

* chore(appflowy_editor): put checkbox testing back

* chore: format code

---------

Co-authored-by: Yijing Huang <hyj891204@gmail.com>
2023-03-09 14:15:22 +08:00
qinluhe 1d28bed281 feat: Initialize appflowy block data and render block list (#1940) 2023-03-09 13:52:48 +08:00
Lucas.Xu 21199c04ac Fix/1936 (#1939)
* fix: [Bug] Code block jump skip blank line

* fix: auto completion interaction

* Revert "fix: [Bug] Code block jump skip blank line"

This reverts commit 5a252bcb18.

* fix: [Bug] Code block jump skip blank line

* fix: number list parse error
2023-03-09 10:10:31 +08:00
Nathan.fooo d3823eb076 refactor: moving grid widgets used by other database view to upper level folder
* refactor: export UIs in flowy_infra_ui

* refactor: move cells to upper level folder

* refactor: move cell blocs
2023-03-09 09:51:04 +08:00
Nathan.fooo 7106195d8a Feat/calendar with backend data (#1930)
* chore: enable calendar view

* chore: update database group listener

* refactor: remove board data controller

* refactor: remove group backend service

* refactor: remove calendar controller

* chore: create default calendar setting

* chore: send calednar setting notifications

* refactor: rename the card in kanban board, prepare to reuse in calendar

* refactor: support custom card cell

* chore: return calendar events

* refactor: remove groupId in card, make card more generic

* chore: display cell

* chore: display three cards in calendar

* chore: create calendar card

* refactor: create row with data

* chore: support create event

* ci: fix tauri build

* chore: disable create calendar
2023-03-08 21:19:44 +08:00
90da54d12f feat: integrate database controller (tauri)
* feat: using controllers in react hooks WIP (#1915)

* chore: add edit / create field test

* chore: add delete field test

* chore: change log class arguments

* chore: delete/create row

* chore: set tracing log to debug level

* fix: filter notification with id

* chore: add get single select type option data

* fix: high cpu usage

* chore: format code

* chore: update tokio version

* chore: config tokio runtime subscriber

* chore: add profiling feature

* chore: setup auto login

* chore: fix tauri build

* chore: (unstable) using controllers

* fix: initially authenticated and serializable fix

* fix: ci warning

* ci: compile error

* fix: new folder trash overflow

* fix: min width for nav panel

* fix: nav panel and main panel animation on hide menu

* fix: highlight active page

* fix: post merge fixes

* fix: post merge fix

* fix: remove warnings

* fix: change IDatabaseField fix eslint errors

* chore: create cell component for each field type

* chore: move cell hook into custom cell component

* chore: refactor row hook

* chore: add tauri clean

---------

Co-authored-by: nathan <nathan@appflowy.io>
Co-authored-by: Nathan.fooo <86001920+appflowy@users.noreply.github.com>

* ci: fix wanrings

---------

Co-authored-by: Askarbek Zadauly <ascarbek@gmail.com>
2023-03-08 10:59:52 +08:00
Lucas.Xu e73870e6e2 fix: auto generator bugs (#1934) 2023-03-08 08:58:28 +08:00
Anderson Cardoso 9e235c578e chore: Update 3: Brazilian Portuguese Localization (#1907)
- Fix typos;
- Fix inconsistencies;
- Removed duplicated block strings.

Note: There are some word by word texts that I still need to figure out how to adapt properly; though, do believe most of strings are good to go!

Please review!
2023-03-07 09:35:04 +08:00
Lucas.Xu 7ff4cecd09 feat: add cover migration for document (#1929)
* feat: add cover migration for document

* fix: should not delete the cover when selecting all

* fix: chinese characters for openai
2023-03-07 09:33:59 +08:00
GouravShDev 675c833f07 feat: add test for appflowy_editor attributes (#1931) 2023-03-07 09:33:35 +08:00
Aman Negi d3ee346cb7 docs(appflowy_flutter) : Update Documentation (#1910)
* docs(appflowy_flutter): Update Documentation

- Added basic information so that new contributors can easily get started with contributions.

* docs(appflowy_flutter): Fix doc issues

- Fixed the wrong Flutter version chip
- Added contributions types
2023-03-06 14:10:10 +08:00
GouravShDev 99f0fad74c feat: add test for appflowy_editor path (#1926) 2023-03-06 10:37:10 +08:00
Nathan.fooo 45045beeb9 chore: rename document test (#1925) 2023-03-05 19:19:40 +08:00
Nathan.fooo 7e7cee4bf4 chore: add tauri database group test (#1924)
* chore: add tauri database group test

* chore: add more tests

* chore: enable run all tests

* chore: rename test folder
2023-03-05 16:26:27 +08:00
Nathan.fooo 8e22ef2230 chore: observer app change (#1922) 2023-03-05 16:13:06 +08:00
Nathan.fooo 6aa9ba28d6 Feat/tauri folder (#1921)
* refactor: folders item

* fix: show get stared page if it's not login

* chore: expose debug pages
2023-03-04 16:15:24 +08:00
Nathan.fooo 205b0fc4a5 Fix/tauri login state (#1919)
* fix: redux serializableCheck

* chore: read login state

* fix: show loading page while checking user state

* chore: cell data parser
2023-03-04 11:03:16 +08:00
Nathan.fooo 3f0d3d802a fix: dart test (#1920) 2023-03-04 08:59:40 +08:00
Nathan.fooo 0f3c6d6a04 Feat/appflowy tauri 3 (#1914) 2023-03-03 20:38:31 +08:00
Nathan.fooo 59a1910b3c Refactor/database classes (#1913) 2023-03-03 18:31:30 +08:00
Muhammad RizwanandLucas.Xu f1316acfcc Implement cover plugin 1868 (#1897)
* implement_cover_plugin_#1868

* code cleanup

* fix: CI issue fix

* fix: cover plugin implementation finalized

* fix: localization fixes

* fix: added add cover button

* chore: optimize the cover plugin code

* feat: auto hide the add button and cover buttons when leaving

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2023-03-02 19:34:22 +08:00
Lucas.Xu fd41459a30 fix: smart edit bugs (#1911) 2023-03-02 19:34:08 +08:00
Lucas.Xu a1a5675875 fix: openai for summarize doesn't work when mutliple selecting (#1904) 2023-03-01 17:38:14 +08:00
Nathan.foooandAskarbek Zadauly f6957fb160 Feat/appflowy tauri 2 (#1902)
* chore: rename classes to models

* refactor: add effects and reducers folder

* chore: update user data storage path

* chore: subscribe callback

* chore: nav items persist, board layout (#1879)

* chore: load workspace items, load folders and pages from workspace, load raw document data, load raw grid data

* chore: clear folders and pages before load, new folder event

* chore: update folder name backend call

* chore: folder expand animation

* chore: hide arrow on empty folder

* chore: Board page layout, board store, board sample data

* chore: board block item

* chore: test db id

* chore: persist new page, persist page rename, create workspace on read error

* chore: boardblockitem details btn

* chore: boardblockitem multiselect data and colors

* chore: board item drag

* chore: drag start on move

* chore: remove databaseId

* chore: remove databaseId

* chore: import service classes into auth hook

* chore: sign out option

* chore: login page event

* chore: signup event

* chore: make workspace hook to use service

* chore: page and folder hooks use backend services

* chore: new folder use backend service

* chore: error handler page

* chore: try catch hooks to show error page

* chore: install i18n package and use flutters i18n files

* fix: signin signup margin

* chore: fix compile errors

* chore: remove unused codes

* chore: open workspace after user register

* chore: open workspace after user register

* chore: add create grid demo

* chore: load the cell data

* chore: print the cell data

* chore: fix project errors

* fix: tauri UI issues (#1899)

* chore: load workspace items, load folders and pages from workspace, load raw document data, load raw grid data

* chore: clear folders and pages before load, new folder event

* chore: update folder name backend call

* chore: folder expand animation

* chore: hide arrow on empty folder

* chore: Board page layout, board store, board sample data

* chore: board block item

* chore: test db id

* chore: persist new page, persist page rename, create workspace on read error

* chore: boardblockitem details btn

* chore: boardblockitem multiselect data and colors

* chore: board item drag

* chore: drag start on move

* chore: remove databaseId

* chore: remove databaseId

* chore: import service classes into auth hook

* chore: sign out option

* chore: login page event

* chore: signup event

* chore: make workspace hook to use service

* chore: page and folder hooks use backend services

* chore: new folder use backend service

* chore: error handler page

* chore: try catch hooks to show error page

* chore: install i18n package and use flutters i18n files

* fix: signin signup margin

* fix: new page overflow with folder

* fix: sign out button

* fix: sign out icon

* chore: floating navigation panel

* refactor: notify with error

* chore: config window size

* fix: test demo error

* chore: update tests

---------

Co-authored-by: Askarbek Zadauly <ascarbek@gmail.com>
2023-02-28 22:42:41 +08:00
Lucas.Xu 085ef8f668 Feature/smart edit v2 (#1880)
* feat: add edit api to openai client

* feat: add translation

* chore: format code

* feat: add smart edit plugin

* fix: close http.client when dispose

* fix: insert openai result to wrong position

* feat: optimize the replace text logic

* test: add test for normalize and getTextInSelection function

* chore: update error message
2023-02-28 14:34:13 +08:00
0x7c01 1945b0fe05 fix: homeMenuResizer position when menu is drawer. (#1896) 2023-02-28 14:33:50 +08:00
Aman NegiandLucas.Xu 2451d3188c feat(shortcuts): Add Shortcuts to Help Menu (#1870)
* feat(shortcuts): Add Shortcuts to Help Menu

- Added option of shorcuts in help menu
- Clicking on the shortcut leads to the `Shortcuts.md` file

* fix: l10n and todos

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2023-02-28 10:38:57 +08:00
Nathan.fooo 4a81fed6e4 fix: group with not support field test (#1890) 2023-02-27 19:00:00 +08:00
dependabot[bot] bf8e36a08b chore(deps): bump openssl-src in /frontend/rust-lib (#1886)
Bumps [openssl-src](https://github.com/alexcrichton/openssl-src-rs) from 111.24.0+1.1.1s to 111.25.0+1.1.1t.
- [Release notes](https://github.com/alexcrichton/openssl-src-rs/releases)
- [Commits](https://github.com/alexcrichton/openssl-src-rs/commits)

---
updated-dependencies:
- dependency-name: openssl-src
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-27 15:40:36 +08:00
Nathan.fooo 61fd608200 Feat/view map database (#1885)
* refactor: rename structs

* chore: read database id from view

* chore: fix open database error because of create a database view for database id

* chore: fix tests

* chore: rename datbase id to view id in flutter

* refactor: move grid and board to database view folder

* refactor: rename functions

* refactor: move calender to datbase view folder

* refactor: rename app_flowy to appflowy_flutter

* chore: reanming

* chore: fix freeze gen

* chore: remove todos

* refactor: view process events

* chore: add link database test

* chore: just open view if there is opened database
2023-02-26 16:27:17 +08:00
6877607c5e chore: calendar plugin backend database data (#1884)
* chore: implement CalendarBloc

* chore: enable save and read the calendar setting

* style: more precise variable name

* chore: backend calendar settings

* chore: protobuf for layout settings

* chore: update test

* chore: Enumerate the LayoutTypePB enum type to get the supported layout types

* fix: deserialize object type is not the same as serialize object type

* chore: add set/get calendar settings event

* ci: fix wanrings

---------

Co-authored-by: nathan <nathan@appflowy.io>
Co-authored-by: vedon <vedon.fu@gmail.com>
2023-02-24 16:59:14 +08:00
Lucas.Xu 01a388c1c4 chore: abstract appflowy editor build function to widget (#1878)
* chore: abstract appflowy editor build function to widget

* feat: refactor the editor state to optional
2023-02-24 09:16:51 +08:00
Kristen McWilliam f4dea31560 fix(network_monitor): add missing case for other network type (#1854)
* fix(network_monitor): add missing case for `other` network type

Missing case causing static analysis error that is blocking the build.

* chore: update depdendency minor versions
2023-02-23 17:07:59 +08:00
Lucas.Xu b356354cd8 feat: support delete consecutive nodes on backend (#1877) 2023-02-21 19:27:52 +08:00
Nathan.fooo f76d722b4c Feat/database view (#1875)
* chore: rename structs

* chore: add todos

* chore: hidden database id

* refactor: add database folder, prepare to extract the view

* refactor: rename grid-model to datbase-model

* ci: fix warnings
2023-02-21 15:47:51 +08:00
Lucas.Xu 346a09b2ae feat: optimize the open ai input status and clear the auto completion… (#1865)
* feat: optimize the open ai input status and clear the auto completion node when exiting the doc

* fix: import error on appflowy editor
2023-02-21 13:25:46 +08:00
Lucas.Xu 3fd4371d2c fix: error happens when parsing readme content (#1873) 2023-02-20 18:28:09 +08:00
Nathan.fooo 8a2f5fe789 Feat/tauri database effects (#1863)
* feat: config database view effects

* chore: add tests

* chore: config jest

* chore: config jest windows

* ci: wanrings

* chore: config folder effect
2023-02-19 14:59:04 +08:00
Lucas.Xu 7c3a823078 feat: add openai service (#1858)
* feat: add openai service

* feat: add openai auto completion plugin

* feat: add visible icon for open ai input field

* chore: optimize user experience

* feat: add auto completion node plugin

* feat: support keep and discard the auto generated text

* fix: can't delete the auto completion node

* feat: disable ai plugins if open ai key is null

* fix: wrong auto completion node card color

* fix: make sure the previous text node is pure when using auto generator
2023-02-16 10:17:08 +08:00
Lucas.Xu 2f9823d12a fix: wrong icon color for grid and board plugin (#1862) 2023-02-15 19:44:18 +08:00
Mayur Mahajan ea9d8d03ad Improve Editing and Navigating shortcuts with Ctrl/Meta (#1845)
* feat: handler for deleting a word

* chore: typo

* test: ctrl and backspace to delete word

* feat: add ctrl alt arrows to select words

* fix: remove print statement

* fix: remove additional shortcut

* fix: handle nodes empty case

* test: edge cases with delete word

* fix: press meta on macos
2023-02-15 14:26:18 +08:00
Kristen McWilliam 64902f763b Add generated Flutter files to gitignore (#1855)
* chore: add generated files to gitignore

* chore: remove gitignored files
2023-02-15 09:46:31 +08:00
Nathan.fooo c1de3ba01b ci: disable eslint (#1857) 2023-02-14 10:28:01 +08:00
Lucas.Xu 72e155f5b9 feat: add open ai key to database (#1852)
* feat: add open ai key to database

* chore: refactor code
2023-02-14 10:04:36 +08:00
Kristen McWilliam 7207e35349 chore: Catch platform errors (#1853)
* refactor(Log): allow passing error and stacktrace to logs

This allows us to log errors and stacktraces in a more structured way.

* feat: catch platform errors

Adds handling for platform errors that are not caught by the
Flutter framework. Doing so will log the error properly and
prevent the app from crashing.
2023-02-14 09:46:25 +08:00
Nathan.foooandAnnie 51041f6860 CI: Appflowy tauri ci (#1851)
* chore: add notification parser

* chore: rename classes to models

* refactor: add effects and reducers folder

* chore: update eslint version

* chore: run npx eslint --fix src

* fix: ParserOptions.project error by ignore linting the  .eslintrc.cjs

* ci: add tauri lint

* ci: disable ubuntu and windows tauri ci

* ci: install

---------

Co-authored-by: Annie <anqi.annie.wang@gmail.com>
2023-02-13 19:24:16 +08:00
Mohammad Zolfaghariandnathan 59cb4a890a fix: empty url group bug (#1755)
* fix: empty url group bug

moving a row to empty url (no status) group would change the row's
url to the group id which is not empty, its a random string.
fixed it with puting a check to fill the url with the empty string
when the group id is equal to empty url group id

* fix: move empty group check to `insert_url_cell`

because evereywhere using `insert_url_cell` we want to check that.

* chore: cargo fmt

---------

Co-authored-by: nathan <nathan@appflowy.io>
2023-02-13 18:55:03 +08:00
Mayur Mahajan 91b942d319 Pressing tab on a todo creates a sub-todo (#1847)
* feat: add tab for lists

* test: tab for lists
2023-02-13 14:47:52 +08:00
Mayur Mahajan 9c8753b191 feat: add new purple theme (#1693)
* feat: add new purple theme

* refactor: change theme color

* fix: contrast issue

* refactor: change the primary btn color
2023-02-13 10:19:02 +08:00
Lucas.Xu 2f803959e7 feat: #1832 Support to import data from Markdown or Text to Document … (#1840)
* feat: #1832 Support to import data from Markdown or Text to Document page

* feat: #1832 Support to import data from Markdown or Text to Document page
2023-02-13 09:51:24 +08:00
Сергей Ворон 592b918175 chore: Update ru-RU.json (#1843) 2023-02-13 09:48:43 +08:00
Nathan.fooo 6bb1c4e89c feat: run rustfmt with custom defined fmt configuration (#1848)
* chore: update rustfmt

* chore: apply rustfmt format
2023-02-13 09:29:49 +08:00
Nathan.fooo e2496e734c docs: udpate user events (#1846) 2023-02-13 08:21:25 +08:00
Kristen McWilliam 6c895ad4fe Refactor: app window logic (#1842)
* feat: add TargetPlatform `isDesktop` helper

Safe and convenient way to check if the current platform is a desktop platform.

* refactor: move window logic to separate class

This is much cleaner and better encapsulated. Sets up an area for
further window management in the future.

Does not try to initialize on mobile or web, which would crash.

* fix: isDesktop doc comment

* fix: set window title

Currently only displays "app_flowy" as the window title. This commit
sets the window title to "AppFlowy" as part of the init process.
2023-02-11 14:16:38 +08:00
Nathan.fooo 8588afcda6 feat: support create document with initial data (#1841) 2023-02-10 22:24:34 +08:00
1ad08ba59d Feat/appflowy tauri UI (#1835)
* chore: create folders

* chore: setup taliwindcss (#1742)

* chore: create folders

* chore: setup taliwindcss

---------

Co-authored-by: nathan <nathan@appflowy.io>
Co-authored-by: Nathan.fooo <86001920+appflowy@users.noreply.github.com>

* feat: greater to blockquote

* fix: local variable 'text' isn't used

* feat: #1061 Support markdown to create a blockquote

* fix: #1732 the actions of an image look different than the ones of a code block

* fix: command of double tilde to strikethrough

* feat: callout (#1732)

* feat: add callout plugin

* refactor: add SelectionMenuItem.node factory

makes calloutMenuItem more readable

* feat: add color picker

* feat: add popover to callout

* feat: add emoji to callout

* fix: store tint name

* fix: remove leading underscores

* fix: revert export of editor_entry

* refactor: move color tint names to appflowy_editor

* fix: #1732 only re-insert text node if it's parent is text node too while deleting

* docs: doc comment for SelectionMenuItem.node

* fix: disable callout plugin

should be re-enabled after #1753 is done

* fix: typo

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>

* Feat/http server adapt (#1754)

* integrate board plugin into document (#1675)

* fix: cursor doesn't blink when opening selection menu

* feat: add board plugin

* feat: integrate board plugin into document

* feat: add i10n and fix known bugs

* feat: support jump to board page on document

* feat: disable editor scroll only when the board plugin is selected

* chore: dart fix

* chore: remove unused files

* fix: dart lint

* Feat/database view (#1765)

* chore: rename flowy-database to flowy-sqlite

* refactor: rename flowy-grid to flowy-database

* refactor: rename grid to database

* refactor: rename GridEvent to DatabaseEvent

* refactor: rename grid_id to database_id

* refactor: rename dart code

* fix: #1763 [Bug] Mouse unable to click a certain area

* fix: potential async errors (#1772)

* feat: Skeleton task (#1775)

* chore: change tauri dev npm script

* chore: setup prettier

* chore: add protobuf type

* chore: move test calls to separate component

* chore: serve assets from app_flowy folder

* chore: import poppins font

* chore: install eslint, remove errors

* placeholder components

* chore: import colors from UI kit, footer panel

* chore: reorganise components

* chore: redux toolkit, navigation folders and files, navigation hooks

* fix: on add folder others close

* fix: tauri_dev task

* fix: restore grid notification

* chore: navigation items events (#1784)

* chore: change tauri dev npm script

* chore: setup prettier

* chore: add protobuf type

* chore: move test calls to separate component

* chore: serve assets from app_flowy folder

* chore: import poppins font

* chore: install eslint, remove errors

* placeholder components

* chore: import colors from UI kit, footer panel

* chore: reorganise components

* chore: redux toolkit, navigation folders and files, navigation hooks

* fix: on add folder others close

* fix: tauri_dev task

* fix: restore grid notification

* chore: shared button

* chore: folder/file popup, rename/duplicate/delete items

* chore: new page types popup

* fix: navitem pages padding

* fix: page click mishandle

* fix: folder click mishandle

* chore: add other page types

* fix: stop propagating on button click

* fix: one alt

* fix: renaming change bg

* refactor: brake Navigation Panel into smaller components

* chore: header panel folder

* chore: focus and select all on rename popup

* chore: add classname to popup

* chore: navigation panel resize

* Feat/appflowy tauri (#1831)

* feat:grid view structure

* feat:add store and refactor grid page

* chore: import icons, resize grid items, change grid items style, add field type icons, reorganize grid toolbar

* feat: auth screens(login, signup and confirm-account) ui done

* chore: add tailwind class sorter and formatted all files

* chore: group svgs into single folder

* chore: resolve warnings in svg files

* fix: use exported fieldType enum

* fix: resolve FieldType referances

* chore: auth pages fixes, replace links, replace buttons, svg fixes, navigate between pages, navigate to homepage on main button click

---------

Co-authored-by: ascarbek <ascarbek@gmail.com>

* ci: wanrings

---------

Co-authored-by: Mikias Tilahun Abebe <mikiastilahun@gmail.com>
Co-authored-by: Andreas Bichinger <andreas.bichinger@gmail.com>
Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
Co-authored-by: Askarbek Zadauly <ascarbek@gmail.com>
2023-02-10 16:26:14 +08:00
Nathan.fooo cbd351453d ci: add tauri ci (#1833)
* ci: add tauri ci

* ci: update install windows scripts

* Update tauri_ci.yaml

* Update tauri_ci.yaml

* ci: update
2023-02-10 14:30:34 +08:00
Richard Shiue 72768a2f77 fix: app version (#1828) 2023-02-09 20:24:43 +08:00
Kristen McWilliamandLucas.Xu 6b8c7d7bc6 fix: use window_manager to set minimum window size (#1791)
* fix: use window_manager to set minimum window size

Resolves #762
Resolves #1559

* refactor: only init WidgetsFlutterBinding once

* fix: set window minimum size

Was meant to be setMinimumSize not setSize

* fix: windows manager crash on macOS

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2023-02-09 14:42:18 +08:00
Lucas.Xu 84ea888c73 fix: update the color of the selection menu item 2023-02-09 13:40:03 +08:00
Mayur Mahajan 95ec607482 [FR] Shortcut for toggling checkbox (#1817)
* feat: shortcut for toggling checkbox

* refactor: separate checkbox event handler

* test: chechbox event handler

* chore: remove unused imports

* refactor: command to ctrl and enter

* refactor: handler to use transactions

* test: checkbox event handler

* chore: remove unused import

* refactor: simplify handler logic
2023-02-09 13:39:25 +08:00
Lucas.Xu 84200ddda4 Merge pull request #1826 from LucasXu0/release/0.1.0
chore: release v0.1.0
2023-02-09 11:16:08 +07:00
Lucas.Xu 60065ddfb2 chore: release v0.1.0 2023-02-09 09:28:53 +08:00
Nathan.fooo e157c19174 docs/update database documentation (#1824)
* chore: update event name

* chore: add events and notifications documentation & remove unused code
2023-02-08 10:40:40 +08:00
Kelvinandnathan 781f0ab88b feat: propagate log from flutter to rust backend (#1723)
* feat: draft commit for getting guidance on send log to backend issue

* feat: modify according to guidance

* feat: add tracing dependencies

* feat: continue implement for sending log to backend

* fix: compile errors

* feat: remove un-necessary code

---------

Co-authored-by: nathan <nathan@appflowy.io>
2023-02-07 22:09:43 +08:00
Nathan.fooo 5004729b72 chore: remove appflowy-board (#1819) 2023-02-07 20:56:11 +08:00
Nathan.fooo e5703f83fb fix: duplicate trash (#1818) 2023-02-07 20:56:01 +08:00
Nathan.fooo e77fef3a19 chore: remove invalid revisions (#1816) 2023-02-07 14:30:25 +08:00
Nathan.fooo 5f760ad578 ci: serial flutter unit test (#1814) 2023-02-07 10:48:28 +08:00
abichingerandLucas.Xu e2f6f68923 feat: node widget action menu (#1783)
* feat: add action menu

* feat: add customActionMenuBuilder

* docs: add comments to action menu classes

* fix: enable callout

* test: add action menu tests

add AppFlowyRenderPluginService.getBuilder

* fix: appflowy_editor exports

* fix: action menu

* chore: add of function to EditorStyle

* fix: action menu test

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2023-02-07 10:03:36 +08:00
Nathan.fooo 3491ffdd08 Fix/read app (#1808)
* fix: filter out the apps that are deleted

* chore: format code style

* chore: fix clippy wanrings
2023-02-06 21:42:01 +08:00
Nathan.fooo 1df2619c9f docs: update code documentation (#1804)
* docs: update code documentation

* chore: fix bloc test

* chore: reduce lock granularity

* chore: fix bloc test
2023-02-06 15:59:30 +08:00
huang12zhengandLucas.Xu 4d5063de6a fix: add a encoder for math, #1795 (#1803)
* fix: add a encoder for math, #1795

* feat: support customzie the node parser for exporting markdown

* chore: fix flutter analyze

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2023-02-06 15:38:49 +08:00
Lucas.Xu 8c0b8a875c Fix grid board plugin issues (#1809)
* fix: #1799 Click : to view the original database UI text

* fix: #1798 Referenced icon is missing
2023-02-06 15:12:01 +08:00
Priestch 897adbe21c chore: fix typo in comment (#1794) 2023-02-06 11:24:57 +08:00
Lucas.Xuandnathan a41894a5ec Fix: 1802 [Bug] Math Equation would be null. #1802 (#1806)
* fix: #1290 [Bug] 300ms delay on buttons in titlebar

* fix: #1802 Math Equation would be null

* fix: retain  as a attribute value

---------

Co-authored-by: nathan <nathan@appflowy.io>
2023-02-06 11:21:05 +08:00
Lucas.Xu cc9bd30356 fix: #1290 [Bug] 300ms delay on buttons in titlebar (#1789) 2023-02-05 16:38:06 +08:00
Nathan.fooo addcabea44 fix: potential fail to read the data of the row (#1793) 2023-02-04 10:07:21 +08:00
Nathan.fooo 4b605b6373 chore: update vscode task (#1790) 2023-02-03 14:13:33 +08:00
Mayur Mahajan 70e8b2e256 feat: add new default shortcuts for exisiting handlers v1 (#1786)
* feat: add new default shortcuts v1

* refactor: remove unnecessary space
2023-02-03 12:14:28 +08:00
Nathan.fooo 069519589e Fix/grid group (#1787)
* ci: config rust log

* chore: rename flowy-sdk to appflowy-core

* fix: create group after editing the url

* fix: start listen on new group

* chore: add tests

* refactor: mock data

* ci: update command
2023-02-02 23:02:49 +08:00
Lucas.Xu d09574951b chore: remove some unnecessary todos (#1785)
* chore: remove some unnecessary todos

* fix: the vertical padding for editor should be 28
2023-02-02 16:29:12 +08:00
Mohammad Zolfaghari c9166137be feat: on enter exit editing property field #1295 (#1747)
* feat: on enter exit editing property field #1295

* chore: use FlowyTextField instead of RoundedInputField

* fix: make all text field border radius 10, added errorBorder

* fix: put cursor position at end of text field

See the related discussion here:
https://github.com/AppFlowy-IO/AppFlowy/pull/1747#issuecomment-1407697216

* chore: make errorText optional on FlowyTextField
2023-02-02 10:29:56 +08:00
Richard Shiue d505314ab1 chore: allow groups to take minimal vertical space if possible (#1736) 2023-02-01 21:42:12 +08:00
Lucas.Xu 2e91dfb4be Integrate Grid into Document (#1759)
* fix: cursor doesn't blink when opening selection menu

* feat: add board plugin

* feat: integrate board plugin into document

* feat: add i10n and fix known bugs

* feat: support jump to board page on document

* feat: disable editor scroll only when the board plugin is selected

* chore: dart fix

* chore: remove unused files

* fix: dart lint

* feat: integrate grid plugin into document

* feat: add more menu to grid plugins

* feat: refactor built-in page plugins, including board and grid

* feat: remove padding set up when plugin type equals to editor
2023-02-01 15:37:45 +08:00
Nathan.fooo 71022ed934 fix: potential async errors (#1772) 2023-01-31 19:30:48 +08:00
Lucas.Xu aa15a45097 Merge pull request #1771 from LucasXu0/fix_1763
fix: #1763 [Bug] Mouse unable to click a certain area
2023-01-31 12:55:15 +07:00
Lucas.Xu fb30989cf8 fix: #1763 [Bug] Mouse unable to click a certain area 2023-01-31 12:10:52 +08:00
Nathan.fooo 5b07656295 Feat/database view (#1765)
* chore: rename flowy-database to flowy-sqlite

* refactor: rename flowy-grid to flowy-database

* refactor: rename grid to database

* refactor: rename GridEvent to DatabaseEvent

* refactor: rename grid_id to database_id

* refactor: rename dart code
2023-01-31 08:28:31 +08:00
Lucas.Xu 5de3912fe3 integrate board plugin into document (#1675)
* fix: cursor doesn't blink when opening selection menu

* feat: add board plugin

* feat: integrate board plugin into document

* feat: add i10n and fix known bugs

* feat: support jump to board page on document

* feat: disable editor scroll only when the board plugin is selected

* chore: dart fix

* chore: remove unused files

* fix: dart lint
2023-01-30 13:22:13 +08:00
Nathan.fooo 0d8adaa921 Feat/http server adapt (#1754) 2023-01-30 11:11:19 +08:00
abichingerandLucas.Xu 000569a836 feat: callout (#1732)
* feat: add callout plugin

* refactor: add SelectionMenuItem.node factory

makes calloutMenuItem more readable

* feat: add color picker

* feat: add popover to callout

* feat: add emoji to callout

* fix: store tint name

* fix: remove leading underscores

* fix: revert export of editor_entry

* refactor: move color tint names to appflowy_editor

* fix: #1732 only re-insert text node if it's parent is text node too while deleting

* docs: doc comment for SelectionMenuItem.node

* fix: disable callout plugin

should be re-enabled after #1753 is done

* fix: typo

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2023-01-30 10:56:19 +08:00
Lucas.Xu 3de4e1cb12 Merge pull request #1694 from abichinger/fix/tilde-command
fix: command of double tilde to strikethrough
2023-01-29 14:11:06 +07:00
Lucas.Xu b34aac3f07 Merge pull request #1744 from LucasXu0/fix_plugins_delete_icon
fix: #1732 the actions of an image look different than the ones of a code block
2023-01-29 11:24:05 +07:00
Lucas.Xu 73ad789bf0 Merge pull request #1696 from abichinger/feat/md-blockquote
feat: greater to blockquote
2023-01-28 16:00:44 +07:00
Lucas.Xu b2bc59c6a1 feat: #1061 Support markdown to create a blockquote 2023-01-28 16:10:52 +08:00
Nathan.fooo 4fb2afe82e chore: notification parser (#1745) 2023-01-27 22:57:23 +08:00
Nathan.fooo 7a750e5255 chore: listen backend notification from Tauri (#1743) 2023-01-27 17:17:51 +08:00
Lucas.Xu baa5d0c59b fix: #1732 the actions of an image look different than the ones of a code block 2023-01-27 17:10:47 +08:00
Richard Shiue 243a781b6c chore: improve grid focus and hover event handling (#1735)
* chore: improve primary cell accessory behavior

* fix: focus border disappearing

* chore: port to GridCellState

* chore: fix typo

* chore: connect popover controller

* chore: final
2023-01-27 10:35:17 +08:00
Nathan.fooo 347245aaa1 Refactor/dart notification (#1740)
* refactor: notification send

* refactor: rename dart-notify to flowy-notification

* ci: fix clippy wanrings

* ci: fix rust code converage
2023-01-26 15:40:23 +08:00
Nathan.fooo 67f07463f0 refactor: tauri folder (#1738) 2023-01-24 14:01:30 +08:00
nathan d2db49efab chore: refactor tauri gen files path 2023-01-22 17:08:56 +08:00
Richard Shiue 0100305af0 chore: improve number format display strings (#1705) 2023-01-20 14:03:23 +08:00
Nathan.fooo 128bf02ffb fix: command fail on linux system (#1731) 2023-01-20 14:02:12 +08:00
Mohammad Zolfaghariandnathan 5d125091d9 feat: support group by url in kanban board (#1687)
* feat: WIP on url controller

* fix: logging correct field

* chore: generate groups

* chore: revert change on URLTypeOptionPB

* chore: add tests + fix move row in group by url

* chore: rename test function

Co-authored-by: nathan <nathan@appflowy.io>
2023-01-19 17:56:55 +08:00
Richard Shiue 9c44b30847 chore: grid field action sheet layout improvement (#1698)
* chore: grid action sheet layout improvement

* style: port away from list
2023-01-19 16:01:37 +08:00
Nathan.fooo aeb29d2e42 ci: fix potential test fail (#1722) 2023-01-19 14:10:57 +08:00
Jon Zubizarreta 115bf20ce0 feat: Basque translation (#1719)
* feat: create new file for Basque

* feat: update lang file values

* fix: change of some translate values

* fix: misspelling

* feat: Added Euskera as an option
2023-01-18 17:20:42 +08:00
abichinger d36aea648c feat: save menu appearance (#1707)
* feat: save menu offset and menu visibility

* refactor: remove collapsedNotifier
2023-01-18 14:30:39 +08:00
abichinger 3d56a0a843 feat: use Gridview.builder (#1708) 2023-01-18 14:21:50 +08:00
Nathan.foooandnathan 1b654e556f chore: config tauri foder (#1721)
Co-authored-by: nathan <nathan@appflowy.io>
2023-01-18 14:21:08 +08:00
Nathan.foooandnathan d77c41013f chore: build tauri from vscode launch (#1720)
* chore: build tauri from vscode launch

* fix: unit test

Co-authored-by: nathan <nathan@appflowy.io>
2023-01-18 11:22:13 +08:00
Onyedika Israel Ukwueze e6b186cc43 Fix/URL cell edit button hover (#1714)
* automated changes to pubspec.lock

* fix: Grid URL property's edit button hovering effect

fixed the problem with a grid with a url property when hovering on any other field in the same row
Initially hovering on other fields triggers the edit button to show up in the URL cell

* update: removed flowy_sdk folder

flowy_sdk folder was renamed to appflowy_backend in one of the earlier commits

* Deleted pubspect.lock

Removed system generated pubspec.lock file

* minor update: changes to pubspec.lock

* update: reverting GeneratedPluginRegistrant.swift to previous form

* update: reverting pubspec upstream url to original url

* update

* update
2023-01-17 21:57:10 +08:00
Nathan.foooandnathan ceb39a4fd9 chore: add tauri build config (#1717)
Co-authored-by: nathan <nathan@appflowy.io>
2023-01-17 21:54:25 +08:00
Nathan.foooandnathan f64346c955 Feat/tauri (#1716)
* feat: support tauri desktop

* chore: support call flowy sdk command

* chore: switch to svelte

* chore: gen js protobuf

* chore: import js protobuf

* chore: call flowy sdk handler

* chore: update scipts

* chore: create index.ts

* chore: track files

* chore: gen ts event

* chore: replace application icon

* chore: migrate to react

* chore: fix wanrings

Co-authored-by: nathan <nathan@appflowy.io>
2023-01-17 16:27:17 +08:00
vedon ba653ff463 ci: support aarch64 (#1710) 2023-01-16 20:47:15 +08:00
Сергей Ворон 834152dffc chore: Update ru-RU.json (#1701) 2023-01-13 21:43:21 +08:00
Richard Shiue 707ea4c8c8 chore: add calendar_view package (#1690)
* chore: add calendar_view package

* chore: improve calendar navigator

* style: improve readability

* chore: localization and moving constants
2023-01-13 21:35:20 +08:00
Nathan.foooandnathan 9215f5188c Feat/restore from snapshot (#1699)
* feat: snapshot for folder

* feat: snapshot for document

Co-authored-by: nathan <nathan@appflowy.io>
2023-01-12 22:31:39 +08:00
Andreas Bichinger 53118a6c51 fix: local variable 'text' isn't used 2023-01-12 12:13:42 +01:00
Nathan.foooandnathan 6a36bcd31d chore: add sqlite feature flag (#1683)
* chore: add sqlite feature flag

* chore: fix clippy warings

* ci: fix clippy

* chore: add rev file persistence

* ci: fix clippy

Co-authored-by: nathan <nathan@appflowy.io>
2023-01-12 13:09:08 +08:00
Nathan.fooo 860c5d100b chore: rename class according to gitbook documentation (#1682) 2023-01-12 10:01:17 +08:00
fe4e28b576 Feat/read cell data for field (#1695)
* chore: read cells for field

* feat: enable read cells for specific field

* ci: fix tests

Co-authored-by: vedon <vedon.fu@gmail.com>
Co-authored-by: nathan <nathan@appflowy.io>
2023-01-12 07:56:46 +08:00
Nathan.foooandnathan b7ba189642 Feat/sort UI (#1642)
* feat: implement sort UI

* chore: config sort listener

* chore: config sort ui

* chore: config sort ui

* feat: support ascending & descending

* fix: reorder rows bugs

* chore: add tests

Co-authored-by: nathan <nathan@appflowy.io>
2023-01-11 17:48:53 +08:00
Andreas Bichinger 85bfb8ad80 fix: command of double tilde to strikethrough 2023-01-10 22:55:32 +01:00
Andreas Bichinger d0fd68da95 feat: greater to blockquote 2023-01-10 22:47:03 +01:00
Lucas.Xu 92baa573e1 Merge pull request #1676 from LucasXu0/text_robot
feat: integrate OpenAI service
2023-01-10 19:47:04 +07:00
Kelvin c46b09f182 feat: support scientific notation and decimal separator (#1688) 2023-01-10 10:28:35 +08:00
Lucas.Xu ab108e1dc1 chore: format dart code 2023-01-10 10:14:52 +08:00
Lucas.Xu fa0a334d6c feat: refactor the gpt3 api and support multi line completion 2023-01-10 10:14:52 +08:00
Lucas.Xu 310236dca0 feat: add sample for open AI editing 2023-01-10 10:14:52 +08:00
Lucas.Xu fc1efeb70b feat: support prompt and suffix 2023-01-10 10:14:52 +08:00
Lucas.Xu 494e31993b feat: add openAI plugin 2023-01-10 10:14:52 +08:00
Lucas.Xu 34d4ea3e54 feat: text robot 2023-01-10 10:14:52 +08:00
Nathan.fooo 91efcafd77 chore: potential fail on trash bloc test (#1686) 2023-01-09 12:17:37 +08:00
Richard Shiueandnathan e8b21955f8 chore: add calendar view plugin along with backend data (#1611)
* chore: create build-in calendar data

* feat: add new calendar view to plugins

* chore: add create calendar page test

* chore: disable for creation for now

* fix: rebase regression

Co-authored-by: nathan <nathan@appflowy.io>
2023-01-08 20:51:19 +08:00
Hagen Hübel e479425297 chore: Removed unused PhantomSnapshotPersistence-import (#1680) 2023-01-08 13:00:15 +08:00
Nathan.fooo 7572321813 chore: auto width GridFieldCellActionSheet (#1673) 2023-01-08 12:59:54 +08:00
37f269b08b Chore/rename flowy sdk (#1679)
* chore: run flutter create on flowy_sdk

* chore: rename flowy-sdk to flowy-core

* chore: rename flowy_sdk to appflowy_backend

* chore: fix windows build

* chore: replace bloctest with test

Co-authored-by: nathan <nathan@appflowy.io>
Co-authored-by: vedon <vedon.fu@gmail.com>
2023-01-08 12:10:53 +08:00
Nathan.fooo a0b5f09b06 chore: save folder snapshot with custom config (#1670) 2023-01-06 14:11:35 +08:00
Nathan.fooo d4946f1194 chore: cell data operation (#1656)
* chore: get all cells for specific field

* chore: auto format clippy wanrings

* chore: get cells for specific field type
2023-01-05 21:27:21 +08:00
Kelvin 7949d3fe4a feat: handling same as Notion when user fills in the mixing of number and text (#1650)
* feat: handling same as Notion when user fills in the mixing of number and text

* feat: remove debug log

* feat: using lazy_static to initialized lazily the regex
2023-01-05 20:59:12 +08:00
Nathan.foooandnathan e9f8796809 fix: update the cell content if input is not valid data (#1652)
Co-authored-by: nathan <nathan@appflowy.io>
2023-01-05 19:29:19 +07:00
Lucas.Xu 3ba3a8dc18 Merge pull request #1655 from LucasXu0/feat_1649
feat: #1649 [FR] Convert quill delta to appflowy document
2023-01-05 18:58:42 +08:00
Lucas.Xu 50f9ac1657 feat: #1649 add document for importing data 2023-01-05 18:12:39 +08:00
Lucas.Xu 2fb0e8da28 feat: #1649 [FR] Convert quill delta to appflowy document 2023-01-05 17:40:03 +08:00
Nathan.foooandnathan b25db83bc0 ci: support linux dart_unit_test (#1653)
Co-authored-by: nathan <nathan@appflowy.io>
2023-01-05 15:25:30 +08:00
Nathan.fooo c181f91175 ci: enable docker build (#1654) 2023-01-05 14:50:37 +08:00
Lucas.Xu e52f59b8c1 Merge pull request #1648 from LucasXu0/feat_1624
#1624 Support Shift + Option + Left/Right Arrow
2023-01-05 10:19:06 +08:00
Lucas.Xu e08ab1fc10 fix: Presses shift + alt + arrow right to select a word 2023-01-05 09:15:18 +08:00
Lucas.Xu d7410cd6e8 feat: #1624 add shortcut for Shift + Option + Left/Right Arrow 2023-01-04 20:10:41 +08:00
Lucas.Xu 06c5f6a790 Merge branch 'main' into feat_1624 2023-01-04 20:04:33 +08:00
Lucas.Xu 35a72f701b feat: #1624 add shortcut for Shift + Option + Left/Right Arrow 2023-01-04 19:58:43 +08:00
Richard Shiue 1a2af1cf49 chore: add close button in select option tags for select option textfield (#1640)
* chore: remove unused isSelected flag

* chore: close popover when an option is deleted

* chore: allow unselecting an option by clicking on a close button
2023-01-04 19:43:49 +08:00
Lucas.Xu 340f27cf87 fix: the settings view of path configuration is not displayed completely (#1647) 2023-01-04 19:41:31 +08:00
Richard Shiue d8958e82c8 fix: menu section scrollcontroller not attached (#1631) 2023-01-04 17:19:54 +08:00
Nathan.fooo a07a180cc8 chore: remove duplicate command in flutter_ci.yaml 2023-01-04 17:16:09 +08:00
Kelvin 3eb1360324 test: add more cases for checkout_box_description_test (#1639) 2023-01-04 16:29:24 +08:00
Nathan.fooo e0a392db00 chore: update flutter_ci.yaml 2023-01-04 16:23:58 +08:00
Lucas.Xu 2006d35a50 fix: the settings view of path configuration is not displayed completely 2023-01-04 15:10:11 +08:00
Richard Shiue 515cd50ac4 chore: grid/kanban toolbar UI improvements (#1634) 2023-01-04 10:21:00 +08:00
Richard Shiue c5b15daac4 fix: don't delete the property if it shouldn't be allowed (#1633) 2023-01-04 10:20:18 +08:00
Kelvinandnathan ddc99d646c test: switching_from_text_to_checkbox (#1632)
* feat: switching from text type to checkbox type

* feat: change to use Err(_)

* test: switch from text to checkbox (still need to cover more cases)

Co-authored-by: nathan <nathan@appflowy.io>
2023-01-04 10:19:07 +08:00
nathan b8f9da6bc7 ci: flutter ci 2023-01-04 00:08:40 +08:00
nathan 2c244203fb ci: rust coverage 2023-01-04 00:02:13 +08:00
nathan bfd2754bda chore: update rust-toolchain 2023-01-03 23:38:57 +08:00
nathan 2ccc2d7c98 ci: remove unsupport syntax 2023-01-03 23:19:30 +08:00
nathan 52c09c863c ci: remove unsupport syntax 2023-01-03 23:13:22 +08:00
Nathan.foooandnathan c1880292ac fix: rust & flutter test (#1644)
Co-authored-by: nathan <nathan@appflowy.io>
2023-01-03 23:10:11 +08:00
Nathan.foooandvedon 0e88d643cf fix: rust and flutter ci (#1643)
Co-authored-by: vedon <vedon.fu@gmail.com>
2023-01-03 22:55:30 +08:00
vedon c932f63e57 Fix/windows build (#1641)
* fix: windows build & refactor release build target

* refactor: merge dart_lint and dart_test into flutter_ci

* refactor: merge rust_lint and rust_test into rust_ci

* chore: update rust_ci.yaml
2023-01-03 22:34:55 +08:00
nathan bce175255f chore: udpate chnagelog 2023-01-03 09:40:46 +08:00
Kelvin 273aaeec75 feat: switching from text type to checkbox type (#1626) 2023-01-02 14:47:21 +08:00
Nathan.fooo 17def1ffab chore: enable rust_unit_test on windows (#1630) 2023-01-02 12:31:31 +08:00
Lucas.Xuandnathan c05c1e191c chore: upgrade minimum support flutter version, >= 3.3.0 (#1551)
* feat: upgrade flutter version to 3.3.0 for appflowy_editor and appflowy_editor_plugin

* feat: upgrade flutter version to 3.3.0 for appflowy

* feat: upgrade flutter version to 3.3.0 for CI

* chore: update flutter version in ci workflows

* chore: fix warnings

* chore: fix unuse import

* chore: support  cargo make dart_unit_test on windows platform

* fix: copy to sandbox errors

Co-authored-by: nathan <nathan@appflowy.io>
2023-01-01 23:26:27 +08:00
nathan 9cd83d02a9 fix: send dart notification 2023-01-01 22:48:28 +08:00
Nathan.foooandnathan 35a93939fe fix: send dart notification (#1629)
Co-authored-by: nathan <nathan@appflowy.io>
2023-01-01 22:34:38 +08:00
Richard Shiue eed6c753dd chore: add spacing between a popover and the edge of the window (#1625) 2023-01-01 22:26:52 +08:00
Nathan.foooandnathan 436291c01a fix: update build.rs with cfg flag control (#1627)
Co-authored-by: nathan <nathan@appflowy.io>
2023-01-01 17:21:19 +08:00
Nathan.foooandnathan a2b5d6fa99 fix: number cell format (#1623)
Co-authored-by: nathan <nathan@appflowy.io>
2022-12-31 08:06:10 +08:00
Lucas.Xu 5c1b084789 fix: cursor doesn't blink when opening selection menu (#1622)
* fix: cursor doesn't blink when opening selection menu

* chore: fix dart lint
2022-12-30 15:59:34 +08:00
Mayur MahajanandLucas.Xu b09f37e025 [FR] Create a new yellow theme for AppFlowy (#1539)
* feat: add dandelion color scheme

* feat: add dandelion to colorsheme

* feat: add setting for choosing theme

* refactor: make theme selection ui consistent

* refactor: remove duplicate  themeTypeLabel

* refactor: translate theme type setting

* refactor: use an extension method for themename

* refactor: change community theme name

* refactor: add theme name in app theme

* refactor: remove theme type from cubit

* refactor: allow theme change with themename

* test: getThemeNameForDisplaying method

* chore: refactor the theme name to const string

* refactor: remove theme translation logic

* fix: add translation for theme label

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2022-12-30 11:44:17 +08:00
Nathan.foooandnathan aa5f052ecf Refactor/crate directory (#1621)
* chore: fix wanrings

* chore: remove protobuf ref in flowy-error-code

* chore: remove protobuf ref in lib-ws

* refactor: remove protobuf trait in flowy http model

* refactor: remove flowy-error-code crate

Co-authored-by: nathan <nathan@appflowy.io>
2022-12-30 11:16:47 +08:00
Kelvin aae8259f63 test: grid switch from date to text test (#1616)
* test: grid switch from date to text test

* test: change back the DateTime format to US
2022-12-30 10:47:32 +08:00
ChristianandDevOwl 52c8f32c82 fix: scrollToBottom for macos (#1610)
Co-authored-by: DevOwl <christian.muehle@devowl.de>
2022-12-28 21:51:19 +08:00
Iñigo Iparragirre 8139856d6d Feat/integrate thiserror anyhow (#1599)
* feat: add thiserror and anyhow dependencies

* feat: add thiserror derive to ErrorCode

* feat: add thiserror derive to FlowyStruct

* feat: update error field

* feat: update lock files

* Revert "feat: update lock files"

This reverts commit df9736b843.

* fix: cargo fmt
2022-12-28 21:49:38 +08:00
Kelvinandnathan b83b18274f test: grid switch from number to text test (#1615)
* test: grid switch from number to text test

* chore: update test

* chore: fix tests

* chore: cargo fmt

Co-authored-by: nathan <nathan@appflowy.io>
2022-12-28 17:22:26 +08:00
Nathan.foooandnathan 05f99ee4a4 fix: potential test fail because of async channel message (#1613)
Co-authored-by: nathan <nathan@appflowy.io>
2022-12-28 10:18:17 +08:00
Kelvin e5717b834d test: grid switch from checkbox to text test (#1612) 2022-12-28 08:25:26 +08:00
Kelvin faf8f73206 test: grid switch from multi select to text test (#1608) 2022-12-27 20:08:32 +08:00
Richard Shiue 70689d3239 feat: show the scrollbars again (#1605)
* fix: padding in grid cell editors

* fix: padding in grid header editor

* fix: padding for row details

* fix: shrink number formt list when possible

* chore: simply use column for select option editor

* fix: make sure that popovers are the edges

* fix: delete button padding

* fix: date and time formats padding mistake
2022-12-27 08:21:30 +08:00
Nathan.foooandnathan f5b7d3951f Feat/sort after change (#1607)
* chore: generate task after row was changed

* chore: config task

* chore: add task test

Co-authored-by: nathan <nathan@appflowy.io>
2022-12-26 20:28:18 +08:00
Nathan.foooandnathan 5a30f46b85 feat: sort cell (#1593)
* chore: call cell decode data

* chore: cache cell decoded data

* chore: update cache cell data

* chore: cache cell data

* refactor: separate cell type option functionalities

* refactor: add TypeOptionCellDataFilter trait

* chore: remove unused codes

* chore: fix wanrings

* chore: add sort tests

* chore: sort single select and multi select

Co-authored-by: nathan <nathan@appflowy.io>
2022-12-24 23:19:11 +08:00
Richard Shiue 6a465ac3e7 fix: button spacing for grid menus (#1602)
* chore: don't put extra space in front of text

* chore: alter button margins for date and time format buttons
2022-12-24 23:01:55 +08:00
e4b07e69fa Added : customize the color and background color of selected text (#1601)
* Added Emoji Support

* Added Color Picker for font color and background color

* chore: revert code

* feat: re-implement the color picker

* test: add test case for adding color

* test: update appflowy_editor test flag

Co-authored-by: Muhammad Rizwan <haris.arshad.2010@gmail.com>
Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2022-12-23 19:57:17 +08:00
Richard Shiue f9cc05319b chore: deprecate InputTextField (#1566) 2022-12-21 19:12:16 +08:00
Eakam 873a46e443 fix: no focus border on date cells (#1469)
* fix: no focus border on date cells

* fix: remove redundant import

* refactor: use existing functionality from GridCellWidget for focus border

* refactor: use requestBeginFocus override instead of GestureDetector
2022-12-21 19:11:49 +08:00
Lucas.Xu 9a387b275f Merge pull request #1595 from LucasXu0/release/0.0.9
chore: release 0.0.9
2022-12-21 11:03:33 +08:00
nathan b6ce7aff9f fix: rename function in test call 2022-12-21 11:03:10 +08:00
Nathan.foooandnathan eee3dcf43a Create test template (#1594)
* refactor: rename functions

* chore: create test templagte

Co-authored-by: nathan <nathan@appflowy.io>
2022-12-21 11:00:08 +08:00
Lucas.Xu 384f8ddcb6 chore: release 0.0.9 2022-12-21 10:35:57 +08:00
Lucas.Xu af716be7aa Merge pull request #1592 from LucasXu0/fix_integration_test
test: fix integration test
2022-12-21 10:09:30 +08:00
Lucas.Xu 86e31253de test: fix integration test 2022-12-21 09:34:14 +08:00
Annie 3f00e78508 chore: update text for folder storage (#1590) 2022-12-20 20:33:41 +08:00
Kelvin 45c73a20c2 test: UnitTest Switching from Multi to text (#1585) 2022-12-20 20:33:17 +08:00
nathan ce86227da3 chore: delete log 2022-12-20 14:45:51 +08:00
Nathan.foooandnathan 4643851b3f refactor: apply cell change set (#1589)
* refactor: update cell changeset trait

* refactor: update cell changeset documentation

Co-authored-by: nathan <nathan@appflowy.io>
2022-12-20 14:40:40 +08:00
Lucas.Xuandnathan 5d7008edd7 feat: Customize the storage folder path (#1538)
* feat: support customize folder path

* feat: add l10n and optimize the logic

* chore: code refactor

* feat: add file read/write permission for macOS

* fix: add toast for restoring path

* feat: fetch apps and show them

* feat: fetch apps and show them

* feat: implement select document logic

* feat: l10n and add select item callback

* feat: add space between tile

* chore: move file exporter to settings

* chore: update UI

* feat: support customizing folder when launching the app

* feat: auto register after customizing folder

* feat: l10n

* feat: l10n

* chore: reinitialize flowy sdk when calling init_sdk

* chore: remove flowysdk const keyword to make sure it can be rebuild

* chore: clear kv values when user logout

* chore: replace current workspace id key in kv.db

* feat: add config.name as a part of seesion_cache_key

* feat: support open folder when launching

* chore: fix some bugs

* chore: dart fix & flutter analyze

* chore: wrap 'sign up with ramdom user' as interface

* feat: dismiss settings view after changing the folder

* fix: read kv value after initializaing with new path

* chore: remove user_id prefix from current workspace key

* fix: move open latest view action to bloc

* test: add test utils for integration tests

* chore: move integration_test to its parent directory

* test: add integration_test ci

* test: switch to B from A, then switch to A again

* chore: fix warings and format code and fix tests

* chore: remove comment out codes

* chore: rename some properties name and optimize the logic

* chore: abstract logic of settings file exporter widget to cubit

* chore: abstract location customizer view from file system view

* chore: abstract settings page index to enum type

* chore: remove the redundant underscore

* test: fix integration test error

* chore: enable integration test for windows and ubuntu

* feat: abstract file picker as service and mock it under integration test

* chore: fix bloc test

Co-authored-by: nathan <nathan@appflowy.io>
2022-12-20 11:14:42 +08:00
Lucas.Xu 079fab6118 Merge pull request #1587 from LucasXu0/fix_#1586
fix: #1586 deleting an image causes the editor to enter a bad state
2022-12-20 09:57:15 +08:00
Lucas.Xu b5f6591daa fix: #1586 deleting an image causes the editor to enter a bad state 2022-12-20 09:37:18 +08:00
Nathan.fooo 9f7f86a49a chore: update test (#1584) 2022-12-20 08:39:46 +08:00
Nathan.foooandnathan 260705280f fix: remove unused steps (#1580)
* chore: remove unused code

* chore: update pin-project version to remove rust lint warnings

* chore: fix potential test failed

Co-authored-by: nathan <nathan@appflowy.io>
2022-12-19 10:47:40 +08:00
Richard Shiue 60d9a6b3ab refactor: appflowy themes (#1567)
* refactor: appflowy themes

* refactor: store ThemeData directly in cubit

* refactor: remove textStyles

* refactor: move AppTheme back into cubit
2022-12-18 16:02:18 +08:00
Nathan.fooo 67e350e797 refactor: provide default type option transform (#1579) 2022-12-17 18:12:47 +08:00
Nathan.fooo 85e489babb Refactor/type option (#1578) 2022-12-17 10:15:13 +08:00
Lucas.Xu d0a914c10f Merge pull request #1573 from juchiast/update-script
build: update install_linux.sh
2022-12-15 20:32:24 +08:00
Duy Do bb312b0a79 build: update install_linux.sh 2022-12-15 11:00:26 +07:00
Lucas.Xu 8cdf6f9ec0 Merge pull request #1557 from rizwan3395/main
fear: support emoji
2022-12-14 11:17:36 +08:00
Nathan.fooo 9666269e27 Merge pull request #1570 from AppFlowy-IO/feat/grid_sort
Feat/grid sort
2022-12-12 21:18:45 +08:00
nathan 31458e817a fix: potential test timeout 2022-12-12 19:49:20 +08:00
nathan 9ba17e004e refactor: type option impl trait 2022-12-12 18:00:09 +08:00
nathan e141e7ee63 chore: add cell cmp trait 2022-12-12 15:29:33 +08:00
Lucas.Xu 3932d0661d feat: update emoji picker UI 2022-12-12 15:10:38 +08:00
Lucas.Xu 98d408af87 fix: flutter analyze problems 2022-12-12 14:51:43 +08:00
Lucas.Xu 15e3d3b486 feat: move emoji_picker to appflowy editor plugin 2022-12-12 11:27:04 +08:00
nathan 1ac1fabb6e chore: add suffix PB to filter structs 2022-12-12 11:25:49 +08:00
nathan 6b4291ccc7 chore: config sort controller 2022-12-12 10:44:34 +08:00
nathan 0cf0b29721 chore: hide the grid blocks from Dart 2022-12-11 13:10:35 +08:00
Nathan.foooandnathan 2a75ad144a fix: the grid cell border color didn't change after switch to new theme (#1564)
Co-authored-by: nathan <nathan@appflowy.io>
2022-12-11 09:06:16 +08:00
Nathan.foooandnathan d487820963 fix: update checklist type option data (#1563)
Co-authored-by: nathan <nathan@appflowy.io>
2022-12-10 20:46:24 +08:00
Richard Shiue 350c55e71d style: rename theme extension file (#1562) 2022-12-10 19:00:46 +08:00
ENsu fd603fe80f test: fix select transform test name (#1560) 2022-12-10 18:39:37 +08:00
nathan 07ebb0cb95 refactor: refresh rows 2022-12-09 20:04:12 +08:00
Muhammad Rizwan 9dfc8dbb43 Update emoji_select_widget.dart 2022-12-09 15:28:15 +05:00
Haris Arshad 3f27576066 added:emoji
added emoji support
2022-12-09 15:22:43 +05:00
Lucas.Xu 247a19b0c6 Merge pull request #1553 from AppFlowy-IO/update_version_num_0.0.8.1
chore: update changelog
2022-12-09 11:32:04 +08:00
Lucas.Xu 8494e0f0a6 Merge pull request #1554 from LucasXu0/rules
feat: add rules for editorstate.apply
2022-12-09 11:31:51 +08:00
Lucas.Xu e94eb20388 chore: disable rules for test cases 2022-12-09 11:06:28 +08:00
Lucas.Xu 8bbae46a7c feat: add rules for editorstate.apply 2022-12-09 10:36:01 +08:00
nathan 8521cc0897 chore: update changelog 2022-12-09 10:18:38 +08:00
Nathan.foooandnathan 12441f1183 fix: auto size filter menu (#1552)
Co-authored-by: nathan <nathan@appflowy.io>
2022-12-09 10:12:45 +08:00
Lucas.Xu 53b7595de8 Merge pull request #1548 from ramanverma2k/fix_1503
Refactor linux workflow & fix 1503
2022-12-09 09:42:59 +08:00
Nathan.foooandnathan 8c225fe547 Feat/restore revision (#1549)
* chore: write snapshot

* chore: add tests

* chore: sync close

* chore: restore from snapshot

* chore: delete invalid revisions after restored from snapshot

* chore: create default view if it fail to deserialize view's revisions when there is no snapshot

* chore: auto generate snapshot

Co-authored-by: nathan <nathan@appflowy.io>
2022-12-09 09:19:47 +08:00
Lucas.Xu a507fb8ec6 Merge pull request #1550 from LucasXu0/fix_windows_copy_paste
fix: Clipboard does not work in Windows #1406
2022-12-08 16:13:01 +08:00
Lucas.Xu 4db5b7a544 chore: typo 2022-12-08 15:50:43 +08:00
Lucas.Xu 1de5b274c3 chore: fix flutter analyze 2022-12-08 15:29:59 +08:00
Lucas.Xu 12b549bad2 fix: Clipboard does not work in Windows #1406 2022-12-08 15:19:21 +08:00
ramanverma2k 6dbb08c2ae fix: move deb control file to linux_installer 2022-12-08 12:04:43 +05:30
Rasyid Miftahul Ihsan 9711d670ca feat: add hover effect when trash icon is selected or hovered. (#1513) 2022-12-08 14:22:27 +08:00
Richard Shiue 442dfe7ef8 feat: switch between light and dark theme based on system settings (#1523)
* feat: allow listening to system for light/dark theme

* chore: implement UI for theme mode setting

* chore: fix translations
2022-12-08 14:21:11 +08:00
Richard Shiue f81d5eb23e fix: location of popover wrt the cell (#1545) 2022-12-08 14:18:57 +08:00
ramanverma2k 32be754eb7 fix: add libkeybinder as a dependency in deb pkg 2022-12-08 10:25:27 +05:30
ramanverma2k 0643e93e94 ci: refactor linux workflow & build an alternative linux package for older distributions. 2022-12-08 10:11:40 +05:30
Lucas.Xu 681d9028e3 Merge pull request #1544 from LucasXu0/fix_1542
fix: [Bug] Text formatting toolbar disappear under layout
2022-12-07 17:09:06 +08:00
Lucas.Xu 49574d3ff0 fix: [Bug] Text formatting toolbar disappear under layout #1542 2022-12-07 10:59:28 +08:00
Lucas.Xu c64b83c2d6 Merge pull request #1541 from LucasXu0/refactor_font_size
chore: refactor font size design
2022-12-06 19:14:14 +08:00
Lucas.Xu 52d65c4c4f Merge pull request #1540 from LucasXu0/missing_code_block_markdown
fix: add missing markdown converter for code block and divider
2022-12-06 18:36:40 +08:00
Lucas.Xu 4da786a439 chore: refactor font size design 2022-12-06 18:36:22 +08:00
Lucas.Xu 114968f2ee fix: markdown decoder and encoder test error 2022-12-06 17:48:00 +08:00
Lucas.Xu 29952bc7fd fix: add missing markdown converter for code block and divider 2022-12-06 17:24:10 +08:00
Nathan.foooandnathan 64ee3140a3 fix: click enter to submit the text content (#1537)
Co-authored-by: nathan <nathan@appflowy.io>
2022-12-05 17:17:33 +08:00
Nathan.foooandnathan a2f9ca2f28 fix: options don't refresh after moving the card (#1536)
Co-authored-by: nathan <nathan@appflowy.io>
2022-12-05 15:23:59 +08:00
Nathan.foooandnathan 72dc0b8f67 fix: filter orders (#1535)
Co-authored-by: nathan <nathan@appflowy.io>
2022-12-05 15:23:48 +08:00
Nathan.foooandnathan 721781f0ae chore: refresh the grid rows after typing text in the text filter (#1533)
Co-authored-by: nathan <nathan@appflowy.io>
2022-12-05 09:55:37 +08:00
Nathan.foooandnathan 0d879a6091 Fix/select option filter (#1530)
* fix: multi select filter bugs

* fix: single select bugs

Co-authored-by: nathan <nathan@appflowy.io>
2022-12-05 09:54:47 +08:00
Nathan.fooo 806a924fff Merge pull request #1528 from AppFlowy-IO/feat/folder_node
Feat/folder node
2022-12-05 09:53:59 +08:00
nathan 9488f42749 chore: macro node 2022-12-04 19:52:24 +08:00
ENsu a2161216d8 feat: can transform options from single & multi select (#1493)
* feat: can transform options from single & multi select

* test: add single-multi switch test
2022-12-04 16:32:37 +08:00
nathan aca9b81c79 chore: implement folder 2022-12-04 09:54:25 +08:00
nathan 81dd353d12 chore: add Node macro 2022-12-03 21:12:45 +08:00
nathan eb1659fd3e chore: add node ast parser 2022-12-03 12:03:52 +08:00
nathan c89277d507 feat: create folder node & add tests 2022-12-02 22:31:03 +08:00
nathan fa01dbb572 chore: rename 2022-12-02 10:44:46 +08:00
Lucas.Xu 63613689cd Merge pull request #1522 from LucasXu0/checklist_typo
chore: checklist.panelTitle typo
2022-12-02 09:43:36 +08:00
Lucas.Xu 89f89e8822 Merge pull request #1519 from LucasXu0/plugin
Separate AppFlowy Editor Plugins
2022-12-02 09:43:20 +08:00
Lucas.Xu a801fa627c chore: checklist.panelTitle typo 2022-12-01 19:42:49 +08:00
Lucas.Xu c6c164d347 feat: integrate divider, code block, and math equation into appflowy 2022-12-01 19:26:00 +08:00
Nathan.fooo be4222a62f Merge pull request #1518 from richardshiue/deprecate-textstyle-getters
chore: remove unused getters for TextStyles
2022-12-01 19:05:49 +08:00
Lucas.Xu 4fa2d6dc2e feat: overwrite paste logic in code block 2022-12-01 18:54:48 +08:00
Lucas.Xu e476337a6a feat: move code block plugin to appflowy editor plugins directory 2022-12-01 18:35:27 +08:00
nathan 44f9254c0a chore: add codegen crate 2022-12-01 15:17:55 +08:00
Lucas.Xu 89becbfe71 feat: add hint text for math equation plugin 2022-12-01 15:08:16 +08:00
Richard Shiue cf42d8cd43 chore: remove unused getters for TextStyles 2022-12-01 14:45:59 +08:00
Lucas.Xu 2b27fe85aa feat: move math equation plugin to appflowy editor plugins directory 2022-12-01 14:44:06 +08:00
nathan 30e7323cf7 chore: delete flowy-tool 2022-12-01 14:42:09 +08:00
Nathan.fooo 39a97499de Merge pull request #1516 from AppFlowy-IO/refactor/backend_event_system 2022-12-01 11:49:48 +08:00
Lucas.Xu 157f929ff9 feat: move divider plugin to appflowy editor plugins directory 2022-12-01 11:14:35 +08:00
nathan bf36ef7fd9 chore: update documentation 2022-12-01 10:59:22 +08:00
Lucas.Xu 7ba638268b feat: init appflowy editor plugins 2022-12-01 09:47:28 +08:00
nathan 96c427e869 refactor: rename flowy-dispatch structs 2022-12-01 09:26:16 +08:00
Nathan.fooo fff4af53e2 Merge pull request #1510 from LucasXu0/fix_1488
fix: [Bug] Inconsistent font weights #1488
2022-11-30 20:31:39 +08:00
Nathan.fooo 6910004003 Merge pull request #1512 from richardshiue/fix-flowytextfield-bugs
fix: flowy text field bugs
2022-11-30 20:30:42 +08:00
Nathan.fooo 7cb964c7a2 Merge pull request #1511 from AppFlowy-IO/feat/checklist_filter
chore: support checklist filter
2022-11-30 20:24:15 +08:00
Richard Shiue eaa1cb5a67 fix: flowy text field bugs 2022-11-30 18:28:51 +08:00
nathan 553cfb3f5e chore: support checklist filter 2022-11-30 16:43:57 +08:00
Lucas.Xu 086195d2cf fix: inconsistent font weight in Chinese #1488 2022-11-30 16:36:53 +08:00
Nathan.fooo 64d9b76e3a Merge pull request #1509 from AppFlowy-IO/feat/remove_debug_info
chore: disable split-debuginfo
2022-11-30 15:59:03 +08:00
Nathan.fooo 5ce8087c91 Merge pull request #1508 from AppFlowy-IO/update_version_num_0.0.8
Update version num 0.0.8
2022-11-30 15:37:14 +08:00
nathan 6117f84373 chore: disable split-debuginfo 2022-11-30 15:36:39 +08:00
Nathan.fooo c8b90974fb Feat: support new column type, Checklist.
Feat/support checklist type
2022-11-30 15:32:19 +08:00
nathan f1a25ab4b7 chore: update version 2022-11-30 15:26:57 +08:00
nathan 2d11d3a68d chore: update version 2022-11-30 15:19:09 +08:00
nathan d89a89131e chore: remove textstyle ref 2022-11-30 15:16:01 +08:00
nathan a800e01f0b chore: config checklist board UI 2022-11-30 15:15:05 +08:00
nathan 29e07089ca fix: checklist cell did get notified after the cell content change 2022-11-30 15:15:03 +08:00
nathan 3cdd6665b3 chore: config checklist cell 2022-11-30 15:14:27 +08:00
nathan 1ba299815e feat: implement checklist UI 2022-11-30 15:14:27 +08:00
nathan b4671c1d99 feat: support checklist 2022-11-30 15:14:27 +08:00
Richard ShiueandNathan.fooo aa85970b0f chore: port more const textstyles to theme provider styles (#1477)
* chore: port more const textstyles to theme provider styles

* style: unnecessary or use shared text fontSizes

* fix: build error from merging main

Co-authored-by: Nathan.fooo <86001920+appflowy@users.noreply.github.com>
2022-11-30 15:11:32 +08:00
Richard Shiue 3b3b61e67c chore: select option cell & header editor UI + move to theme.of(context).texttheme (#1483)
* chore: improvements for suffix text in textfields

* chore: port more const textstyles to theme provider styles

* chore: select option editor UI improvements
2022-11-30 15:02:41 +08:00
Richard Shiue f02e77fcd8 fix: onPrimary and onPrimary colors (#1499) 2022-11-30 15:00:14 +08:00
Lucas.Xu 5adf3139ba Merge pull request #1507 from LucasXu0/remove_svg_function
Remove svg function
2022-11-30 13:44:41 +08:00
Lucas.Xu c127fd28b8 chore: fix flutter analyze 2022-11-30 13:23:03 +08:00
Lucas.Xu d7783a6440 chore: remove duplicated svg function 2022-11-30 13:22:16 +08:00
Lucas.Xu fba69767a7 Merge pull request #1495 from LucasXu0/customize_font_size
Customize Font Size In AppFlowy #1479
2022-11-29 13:45:33 +08:00
Nathan.foooandnathan acaaabee2a chore: add transform field tests (#1504)
Co-authored-by: nathan <nathan@appflowy.io>
2022-11-29 13:34:10 +08:00
Lucas.Xu 925ef48a83 fix: build macos install ci error (#1502) 2022-11-29 09:38:06 +08:00
Nathan.foooandnathan 80d1cbabe0 feat: support multi-select option filter (#1501)
Co-authored-by: nathan <nathan@appflowy.io>
2022-11-28 22:49:13 +08:00
Lucas.Xu 43031b50eb feat: update bold text style 2022-11-28 21:31:33 +08:00
Lucas.Xu 52ba0c0092 fix: the cursor shows sometimes when tapping the checkbox 2022-11-28 18:44:48 +08:00
Lucas.Xu 0e55cd07a4 chore: fix typo 2022-11-28 18:32:32 +08:00
Lucas.Xu 14ac2db06d Merge remote-tracking branch 'origin/main' into customize_font_size 2022-11-28 18:11:06 +08:00
Lucas.Xu a162b02476 feat: convert provider to cubit #1479 2022-11-28 18:02:05 +08:00
Lucas.Xu 04e14c6bba fix: transparent color should be treated as a highlight color 2022-11-28 16:22:10 +08:00
Lucas.Xu d75645c4bc fix: remove the unnecessary text style update 2022-11-28 16:17:46 +08:00
Lucas.Xu 6d6e61956a feat: Customize Font Size In AppFlowy #1479 2022-11-28 15:34:55 +08:00
Lucas.Xu 37b119172b feat: Customize Font Size In AppFlowy #1479 2022-11-28 13:26:43 +08:00
Lucas.Xu 0ba26e0a84 feat: Customize Font Size In AppFlowy #1479 2022-11-28 10:37:37 +08:00
3199 changed files with 121304 additions and 81080 deletions

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
frontend/app_flowy/
frontend/appflowy_flutter/
frontend/scripts/
frontend/rust-lib/target
shared-lib/target/
+5 -3
View File
@@ -11,10 +11,11 @@ on:
- "main"
- "release/*"
paths:
- "frontend/app_flowy/packages/appflowy_editor/**"
- "frontend/appflowy_flutter/packages/appflowy_editor/**"
env:
CARGO_TERM_COLOR: always
FLUTTER_VERSION: "3.3.9"
jobs:
tests:
@@ -30,11 +31,11 @@ jobs:
- uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: "3.0.5"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Run FlowyEditor tests
working-directory: frontend/app_flowy/packages/appflowy_editor
working-directory: frontend/appflowy_flutter/packages/appflowy_editor
run: |
flutter pub get
flutter format --set-exit-if-changed .
@@ -44,6 +45,7 @@ jobs:
- uses: codecov/codecov-action@v3
with:
name: appflowy_editor
flags: appflowy_editor
env_vars: ${{ matrix.os }}
fail_ci_if_error: true
verbose: true
-86
View File
@@ -1,86 +0,0 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
name: Flutter lint
on:
push:
branches:
- "main"
- "release/*"
paths:
- "frontend/app_flowy/**"
pull_request:
branches:
- "main"
- "release/*"
paths:
- "frontend/app_flowy/**"
env:
CARGO_TERM_COLOR: always
jobs:
flutter-analyze:
name: flutter analyze
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- uses: subosito/flutter-action@v1
with:
flutter-version: "3.0.5"
channel: "stable"
- uses: actions-rs/toolchain@v1
with:
toolchain: "stable-2022-04-07"
- name: Cache Cargo
id: cache-cargo
uses: actions/cache@v2
with:
path: |
~/.cargo
key: ${{ runner.os }}-cargo-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
- name: Cache Rust
id: cache-rust-target
uses: actions/cache@v2
with:
path: |
frontend/rust-lib/target
shared-lib/target
key: ${{ runner.os }}-rust-rust-lib-share-lib-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
- if: steps.cache-cargo.outputs.cache-hit != 'true'
name: Rust Deps
working-directory: frontend
run: |
cargo install cargo-make
- name: Cargo make flowy dev
working-directory: frontend
run: |
cargo make appflowy-deps-tools
- name: Flutter Deps
run: flutter packages pub get
working-directory: frontend/app_flowy
- name: Build FlowySDK
working-directory: frontend
run: |
cargo make --profile development-linux-x86_64 appflowy-sdk-dev
- name: Flutter Code Generation
working-directory: frontend/app_flowy
run: |
flutter packages pub run easy_localization:generate -f keys -o locale_keys.g.dart -S assets/translations -s en.json
flutter packages pub run build_runner build --delete-conflicting-outputs
- name: Run Flutter Analyzer
working-directory: frontend/app_flowy
run: flutter analyze
-80
View File
@@ -1,80 +0,0 @@
name: Frontend test
on:
push:
branches:
- "main"
- "release/*"
paths:
- "frontend/app_flowy/**"
pull_request:
branches:
- "main"
- "release/*"
paths:
- "frontend/app_flowy/**"
env:
CARGO_TERM_COLOR: always
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: "stable-2022-04-07"
- uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: "3.0.5"
cache: true
- name: Cache Cargo
uses: actions/cache@v2
with:
path: |
~/.cargo
key: ${{ runner.os }}-cargo-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
- name: Cache Rust
id: cache-rust-target
uses: actions/cache@v2
with:
path: |
frontend/rust-lib/target
shared-lib/target
key: ${{ runner.os }}-rust-rust-lib-share-lib-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
- if: steps.cache-cargo.outputs.cache-hit != 'true'
name: Rust Deps
working-directory: frontend
run: |
cargo install cargo-make
cargo make appflowy-deps-tools
- name: Flutter Deps
working-directory: frontend/app_flowy
run: |
flutter config --enable-linux-desktop
- name: Build Test lib
working-directory: frontend
run: |
cargo make --profile test-linux build-test-lib
- name: Flutter Code Generation
working-directory: frontend/app_flowy
run: |
flutter packages pub get
flutter packages pub run easy_localization:generate -f keys -o locale_keys.g.dart -S assets/translations -s en.json
flutter packages pub run build_runner build --delete-conflicting-outputs
- name: Run bloc tests
working-directory: frontend/app_flowy
run: |
flutter pub get
flutter test
@@ -1,89 +1,87 @@
name: CI
name: Flutter-CI
on:
push:
branches:
- "main"
- "release/*"
paths:
- "frontend/**"
pull_request:
branches:
- "main"
- "release/*"
paths:
- "frontend/**"
env:
FLUTTER_VERSION: "3.3.9"
RUST_TOOLCHAIN: "1.65"
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
include:
- os: ubuntu-latest
flutter_profile: development-linux-x86_64
target: x86_64-unknown-linux-gnu
- os: macos-latest
flutter_profile: development-mac-x86_64
target: x86_64-apple-darwin
- os: windows-latest
flutter_profile: development-windows-x86
target: x86_64-pc-windows-msvc
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Checkout source code
uses: actions/checkout@v2
- id: rust_toolchain
- name: Install Rust toolchain
id: rust_toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: "stable-2022-04-07"
toolchain: ${{ env.RUST_TOOLCHAIN }}
target: ${{ matrix.target }}
override: true
profile: minimal
- id: flutter
- name: Install flutter
id: flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
flutter-version: "3.0.5"
- name: Cache Cargo
id: cache-cargo
uses: actions/cache@v2
- uses: Swatinem/rust-cache@v2
with:
path: |
~/.cargo
key: ${{ runner.os }}-cargo-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
prefix-key: ${{ matrix.job.os }}
- name: Cache Rust
uses: actions/cache@v2
with:
path: |
frontend/rust-lib/target
shared-lib/target
key: ${{ runner.os }}-rust-rust-lib-share-lib-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
- name: Setup Environment
- name: Install prerequisites
working-directory: frontend
run: |
cargo install --force cargo-make
cargo install --force duckscript_cli
if [ "$RUNNER_OS" == "Linux" ]; then
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
sudo apt-get update
sudo apt-get install -y dart curl build-essential libsqlite3-dev libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
sudo apt-get install keybinder-3.0
elif [ "$RUNNER_OS" == "Windows" ]; then
vcpkg integrate install
elif [ "$RUNNER_OS" == "macOS" ]; then
echo 'do nothing'
fi
cargo make appflowy-flutter-deps-tools
shell: bash
- if: steps.cache-cargo.outputs.cache-hit != 'true'
name: Deps
working-directory: frontend
run: |
cargo install cargo-make
cargo install duckscript_cli
- name: Cargo make appflowy-deps-tools
working-directory: frontend
run: |
cargo make appflowy-deps-tools
- name: Config Flutter
- name: Enable Flutter Desktop
run: |
if [ "$RUNNER_OS" == "Linux" ]; then
flutter config --enable-linux-desktop
@@ -94,7 +92,16 @@ jobs:
fi
shell: bash
- name: Build
- name: Build AppFlowy
working-directory: frontend
run: |
cargo make --profile ${{ matrix.flutter_profile }} appflowy-dev
- name: Flutter Analyzer
working-directory: frontend/appflowy_flutter
run: flutter analyze
- name: Run Flutter unit tests
working-directory: frontend
run: |
cargo make dart_unit_test
+128
View File
@@ -0,0 +1,128 @@
name: integration test
on:
push:
branches:
- "main"
- "release/*"
paths:
- "frontend/appflowy_flutter/**"
pull_request:
branches:
- "main"
- "release/*"
paths:
- "frontend/appflowy_flutter/**"
env:
CARGO_TERM_COLOR: always
jobs:
tests:
strategy:
matrix:
os: [macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: "stable-2022-04-07"
- uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: "3.3.9"
cache: true
- name: Cache Cargo
uses: actions/cache@v2
with:
path: |
~/.cargo
key: ${{ runner.os }}-cargo-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
- name: Cache Rust
id: cache-rust-target
uses: actions/cache@v2
with:
path: |
frontend/rust-lib/target
shared-lib/target
key: ${{ runner.os }}-rust-rust-lib-share-lib-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
- name: Setup Environment
run: |
if [ "$RUNNER_OS" == "Linux" ]; then
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
sudo apt-get update
sudo apt-get install -y dart curl build-essential libsqlite3-dev libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
sudo apt-get install keybinder-3.0
elif [ "$RUNNER_OS" == "Windows" ]; then
vcpkg integrate install
cargo install --force duckscript_cli
elif [ "$RUNNER_OS" == "macOS" ]; then
echo 'do nothing'
fi
shell: bash
- if: steps.cache-cargo.outputs.cache-hit != 'true'
name: Rust Deps
working-directory: frontend
run: |
cargo install cargo-make
cargo make appflowy-flutter-deps-tools
- name: Build Test lib
working-directory: frontend
run: |
if [ "$RUNNER_OS" == "Linux" ]; then
cargo make --profile production-linux-x86_64 appflowy
elif [ "$RUNNER_OS" == "macOS" ]; then
cargo make --profile production-mac-x86_64 appflowy
elif [ "$RUNNER_OS" == "Windows" ]; then
cargo make --profile production-windows-x86 appflowy
fi
- name: Config Flutter
run: |
if [ "$RUNNER_OS" == "Linux" ]; then
flutter config --enable-linux-desktop
elif [ "$RUNNER_OS" == "macOS" ]; then
flutter config --enable-macos-desktop
elif [ "$RUNNER_OS" == "Windows" ]; then
flutter config --enable-windows-desktop
fi
shell: bash
- name: Flutter Code Generation
working-directory: frontend/appflowy_flutter
run: |
flutter packages pub get
flutter packages pub run easy_localization:generate -f keys -o locale_keys.g.dart -S assets/translations -s en.json
flutter packages pub run build_runner build --delete-conflicting-outputs
- name: Run AppFlowy tests
working-directory: frontend/appflowy_flutter
run: |
if [ "$RUNNER_OS" == "Linux" ]; then
flutter test integration_test -d Linux --coverage
elif [ "$RUNNER_OS" == "macOS" ]; then
flutter test integration_test -d macOS --coverage
elif [ "$RUNNER_OS" == "Windows" ]; then
flutter test integration_test -d Windows --coverage
fi
shell: bash
- uses: codecov/codecov-action@v3
with:
name: appflowy
flags: appflowy
env_vars: ${{ matrix.os }}
fail_ci_if_error: true
verbose: true
+278 -195
View File
@@ -5,6 +5,10 @@ on:
tags:
- '*'
env:
FLUTTER_VERSION: "3.3.9"
RUST_TOOLCHAIN: "1.65"
jobs:
create-release:
runs-on: ubuntu-latest
@@ -31,211 +35,50 @@ jobs:
release_name: v${{ github.ref }}
body_path: ${{ env.RELEASE_NOTES_PATH }}
build-linux-x86:
runs-on: ubuntu-latest
build-for-windows:
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
needs: create-release
env:
LINUX_APP_RELEASE_PATH: frontend/app_flowy/product/${{ github.ref_name }}/linux/Release
LINUX_ZIP_NAME: AppFlowy-linux-x86.tar.gz
LINUX_PACKAGE_NAME: AppFlowy_${{ github.ref_name }}_linux-amd64.deb
WINDOWS_APP_RELEASE_PATH: frontend\appflowy_flutter\product\${{ github.ref_name }}\windows
WINDOWS_ZIP_NAME: AppFlowy_${{ github.ref_name }}_windows-x86_64.zip
WINDOWS_INSTALLER_NAME: AppFlowy_${{ github.ref_name }}_windows-x86_64
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
- { target: x86_64-pc-windows-msvc, os: windows-2019 }
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup environment - Rust and Cargo
uses: actions-rs/toolchain@v1
with:
toolchain: 'stable-2022-04-07'
- name: Setup environment - Flutter
uses: subosito/flutter-action@v2
with:
channel: 'stable'
flutter-version: '3.0.5'
- name: Pre build
working-directory: frontend
run: |
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
sudo apt-get update
sudo apt-get install -y build-essential libsqlite3-dev libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
sudo apt-get install keybinder-3.0
source $HOME/.cargo/env
cargo install --force cargo-make
cargo install --force duckscript_cli
cargo make appflowy-deps-tools
- name: Build Linux app
working-directory: frontend
run: |
flutter config --enable-linux-desktop
cargo make --env APP_VERSION=${{ github.ref_name }} --profile production-linux-x86_64 appflowy
- name: Build Linux package
working-directory: ${{ env.LINUX_APP_RELEASE_PATH }}
run: |
mkdir -p package/opt && mv AppFlowy package/opt/
cd package && mkdir DEBIAN
# Create control file
printf 'Package: AppFlowy
Version: %s
Architecture: amd64
Essential: no
Priority: optional
Maintainer: AppFlowy
Description: An Open Source Alternative to Notion\n' "${{ github.ref_name }}" > DEBIAN/control
# postinst script for creating symlink
printf '#!/bin/bash
if [ -e /usr/local/bin/appflowy ]; then
echo "Symlink already exists, skipping."
else
echo "Creating Symlink in /usr/local/bin/appflowy"
ln -s /opt/AppFlowy/app_flowy /usr/local/bin/appflowy
fi' > DEBIAN/postinst
chmod 0755 DEBIAN/postinst
# postrm script for cleaning up residuals
printf '#!/bin/bash
if [ -e /usr/local/bin/appflowy ]; then
rm /usr/local/bin/appflowy
fi' > DEBIAN/postrm
chmod 0755 DEBIAN/postrm
mkdir -p usr/share/applications
# Update Exec & icon path in desktop entry
grep -rl "\[CHANGE_THIS\]" ./opt/AppFlowy/appflowy.desktop.temp | xargs sed -i "s/\[CHANGE_THIS\]/\/opt/"
# Add desktop entry in package
mv ./opt/AppFlowy/appflowy.desktop.temp ./usr/share/applications/appflowy.desktop
# Build
cd ../ && dpkg-deb --build --root-owner-group package ${{ env.LINUX_PACKAGE_NAME }}
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ${{ env.LINUX_APP_RELEASE_PATH }}/${{ env.LINUX_ZIP_NAME }}
asset_name: ${{ env.LINUX_ZIP_NAME }}
asset_content_type: application/octet-stream
- name: Upload Release Asset Install Package
id: upload-release-asset-install-package
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ${{ env.LINUX_APP_RELEASE_PATH }}/${{ env.LINUX_PACKAGE_NAME }}
asset_name: ${{ env.LINUX_PACKAGE_NAME }}
asset_content_type: application/octet-stream
build-macos-x86_64:
runs-on: macos-latest
needs: create-release
env:
MACOS_APP_RELEASE_PATH: frontend/app_flowy/product/${{ github.ref_name }}/macos/Release
MACOS_X86_ZIP_NAME: Appflowy-macos-x86_64.zip
MACOS_DMG_NAME: Appflowy-macos-x86_64-installer
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup environment - Rust and Cargo
uses: actions-rs/toolchain@v1
with:
toolchain: 'stable-2022-04-07'
- name: Setup environment - Flutter
uses: subosito/flutter-action@v2
with:
channel: 'stable'
flutter-version: '3.0.5'
- name: Pre build
working-directory: frontend
run: |
source $HOME/.cargo/env
cargo install --force cargo-make
cargo install --force duckscript_cli
cargo make appflowy-deps-tools
- name: Build macOS app for x86_64
working-directory: frontend
run: |
flutter config --enable-macos-desktop
cargo make --env APP_VERSION=${{ github.ref_name }} --profile production-mac-x86_64 appflowy
- name: Create MacOS dmg
working-directory: frontend
run: |
brew install create-dmg
create-dmg \
--volname ${{ env.MACOS_DMG_NAME }} \
--hide-extension "AppFlowy.app" \
--background scripts/dmg_assets/AppFlowyInstallerBackground.jpg \
--window-size 600 450 \
--icon-size 94 \
--icon "AppFlowy.app" 141 249 \
--app-drop-link 458 249 \
"${{ env.MACOS_APP_RELEASE_PATH }}/${{ env.MACOS_DMG_NAME }}.dmg" \
"${{ env.MACOS_APP_RELEASE_PATH }}/AppFlowy.app"
- name: Archive macOS app
working-directory: ${{ env.MACOS_APP_RELEASE_PATH }}
run: zip --symlinks -qr ${{ env.MACOS_X86_ZIP_NAME }} AppFlowy.app
- name: Upload Release Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ${{ env.MACOS_APP_RELEASE_PATH }}/${{ env.MACOS_X86_ZIP_NAME }}
asset_name: ${{ env.MACOS_X86_ZIP_NAME }}
asset_content_type: application/octet-stream
- name: Upload DMG Release Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ${{ env.MACOS_APP_RELEASE_PATH }}/${{ env.MACOS_DMG_NAME }}.dmg
asset_name: ${{ env.MACOS_DMG_NAME }}.dmg
asset_content_type: application/octet-stream
build-windows-x86_64:
runs-on: windows-latest
needs: create-release
env:
WINDOWS_APP_RELEASE_PATH: frontend\app_flowy\product\${{ github.ref_name }}\windows
WINDOWS_ZIP_NAME: AppFlowy-windows-x86_64.zip
WINDOWS_INSTALLER_NAME: AppFlowy-${{ github.ref_name }}-windows-x86_64
steps:
- name: Checkout
- name: Checkout source code
uses: actions/checkout@v3
- name: Setup environment - Rust and Cargo
uses: actions-rs/toolchain@v1
with:
toolchain: 'stable-2022-04-07'
- name: Setup environment - Flutter
- name: Install flutter
uses: subosito/flutter-action@v2
with:
channel: 'stable'
flutter-version: '3.0.5'
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Pre build
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
target: ${{ matrix.job.target }}
override: true
components: rustfmt
profile: minimal
- uses: Swatinem/rust-cache@v2
with:
prefix-key: appflowy-lib-cache
key: ${{ matrix.job.os }}-${{ matrix.job.target }}
- name: Install prerequisites
working-directory: frontend
run: |
vcpkg integrate install
cargo install --force cargo-make
cargo install --force duckscript_cli
cargo make appflowy-deps-tools
- name: Build Windows app
working-directory: frontend
@@ -243,11 +86,12 @@ jobs:
flutter config --enable-windows-desktop
cargo make --env APP_VERSION=${{ github.ref_name }} --profile production-windows-x86 appflowy
- uses: vimtor/action-zip@v1
- name: Archive Asset
uses: vimtor/action-zip@v1
with:
files: ${{ env.WINDOWS_APP_RELEASE_PATH }}\
dest: ${{ env.WINDOWS_APP_RELEASE_PATH }}\${{ env.WINDOWS_ZIP_NAME }}
- name: Copy installer config & icon file
working-directory: frontend
run: |
@@ -258,7 +102,7 @@ jobs:
run: |
iscc /F${{ env.WINDOWS_INSTALLER_NAME }} inno_setup_config.iss /DAppVersion=${{ github.ref_name }}
- name: Upload Release Asset
- name: Upload Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
@@ -279,3 +123,242 @@ jobs:
asset_path: ${{ env.WINDOWS_APP_RELEASE_PATH }}\Output\${{ env.WINDOWS_INSTALLER_NAME }}.exe
asset_name: ${{ env.WINDOWS_INSTALLER_NAME }}.exe
asset_content_type: application/octet-stream
build-for-macOS:
name: ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-args }}]
runs-on: ${{ matrix.job.os }}
needs: create-release
env:
MACOS_APP_RELEASE_PATH: frontend/appflowy_flutter/product/${{ github.ref_name }}/macos/Release
MACOS_X86_ZIP_NAME: AppFlowy_${{ github.ref_name }}_macos-x86_64.zip
MACOS_DMG_NAME: AppFlowy_${{ github.ref_name }}_macos-x86_64
strategy:
fail-fast: false
matrix:
job:
- { target: x86_64-apple-darwin, os: macos-10.15, extra-build-args: "" }
steps:
- name: Checkout source code
uses: actions/checkout@v3
- name: Install flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
target: ${{ matrix.job.target }}
override: true
components: rustfmt
profile: minimal
- uses: Swatinem/rust-cache@v2
with:
prefix-key: appflowy-lib-cache
key: ${{ matrix.job.os }}-${{ matrix.job.target }}
- name: Install prerequisites
working-directory: frontend
run: |
cargo install --force cargo-make
cargo install --force duckscript_cli
- name: Build AppFlowy
working-directory: frontend
run: |
flutter config --enable-macos-desktop
cargo make --env APP_VERSION=${{ github.ref_name }} --profile production-mac-x86_64 appflowy
- name: Create macOS dmg
run: |
brew install create-dmg
create-dmg \
--volname ${{ env.MACOS_DMG_NAME }} \
--hide-extension "AppFlowy.app" \
--background frontend/scripts/dmg_assets/AppFlowyInstallerBackground.jpg \
--window-size 600 450 \
--icon-size 94 \
--icon "AppFlowy.app" 141 249 \
--app-drop-link 458 249 \
"${{ env.MACOS_APP_RELEASE_PATH }}/${{ env.MACOS_DMG_NAME }}.dmg" \
"${{ env.MACOS_APP_RELEASE_PATH }}/AppFlowy.app"
- name: Archive Asset
working-directory: ${{ env.MACOS_APP_RELEASE_PATH }}
run: zip --symlinks -qr ${{ env.MACOS_X86_ZIP_NAME }} AppFlowy.app
- name: Upload Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ${{ env.MACOS_APP_RELEASE_PATH }}/${{ env.MACOS_X86_ZIP_NAME }}
asset_name: ${{ env.MACOS_X86_ZIP_NAME }}
asset_content_type: application/octet-stream
- name: Upload DMG Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ${{ env.MACOS_APP_RELEASE_PATH }}/${{ env.MACOS_DMG_NAME }}.dmg
asset_name: ${{ env.MACOS_DMG_NAME }}.dmg
asset_content_type: application/octet-stream
build-for-linux:
name: ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-args }}]
runs-on: ${{ matrix.job.os }}
needs: create-release
env:
LINUX_APP_RELEASE_PATH: frontend/appflowy_flutter/product/${{ github.ref_name }}/linux/Release
LINUX_ZIP_NAME: AppFlowy_${{ matrix.job.target }}_${{ matrix.job.os }}.tar.gz
LINUX_PACKAGE_NAME: AppFlowy_${{ github.ref_name }}_${{ matrix.job.os }}.deb
# PKG_CONFIG_SYSROOT_DIR: /
strategy:
fail-fast: false
matrix:
job:
- { arch: x86_64, target: x86_64-unknown-linux-gnu, os: ubuntu-20.04, extra-build-args: "", flutter_profile: production-linux-x86_64 }
# - { arch: aarch64, target: aarch64-unknown-linux-gnu, os: ubuntu-20.04, extra-build-args: "", flutter_profile: production-linux-aarch64 }
- { arch: x86_64, target: x86_64-unknown-linux-gnu, os: ubuntu-18.04, extra-build-args: "", flutter_profile: production-linux-x86_64}
steps:
- name: Checkout source code
uses: actions/checkout@v3
- name: Install flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
target: ${{ matrix.job.target }}
override: true
components: rustfmt
profile: minimal
- uses: Swatinem/rust-cache@v2
with:
prefix-key: appflowy-lib-cache
key: ${{ matrix.job.os }}-${{ matrix.job.target }}
- name: Install prerequisites
working-directory: frontend
run: |
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
sudo apt-get update
sudo apt-get install -y build-essential libsqlite3-dev libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
sudo apt-get install keybinder-3.0
source $HOME/.cargo/env
cargo install --force cargo-make
cargo install --force duckscript_cli
rustup target add ${{ matrix.job.target }}
- name: Install gcc-aarch64-linux-gnu
if: ${{ matrix.job.target == 'aarch64-unknown-linux-gnu' }}
working-directory: frontend
run: |
sudo apt-get install -qy binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
- name: Build AppFlowy
working-directory: frontend
run: |
flutter config --enable-linux-desktop
cargo make --env APP_VERSION=${{ github.ref_name }} --profile ${{ matrix.job.flutter_profile}} appflowy
- name: Archive Assert
working-directory: ${{ env.LINUX_APP_RELEASE_PATH }}
run: tar -czf ${{ env.LINUX_ZIP_NAME }} *
- name: Configuring Linux Package
working-directory: frontend
run: |
mkdir -p ../${{ env.LINUX_APP_RELEASE_PATH }}/package/opt
mkdir -p ../${{ env.LINUX_APP_RELEASE_PATH }}/package/usr/share/applications
cp -r ./scripts/linux_installer ../${{ env.LINUX_APP_RELEASE_PATH }}/package/DEBIAN
cd ../${{ env.LINUX_APP_RELEASE_PATH }}/package/DEBIAN
grep -rl "\[CHANGE_THIS\]" ./control | xargs sed -i "s/\[CHANGE_THIS\]/${{ github.ref_name }}/"
chmod 0755 {postinst,postrm}
- name: Build Linux package
working-directory: ${{ env.LINUX_APP_RELEASE_PATH }}
run: |
mv AppFlowy package/opt/
cd package
# Update Exec & icon path in desktop entry
grep -rl "\[CHANGE_THIS\]" ./opt/AppFlowy/appflowy.desktop.temp | xargs sed -i "s/\[CHANGE_THIS\]/\/opt/"
# Add desktop entry in package
mv ./opt/AppFlowy/appflowy.desktop.temp ./usr/share/applications/appflowy.desktop
# Build
cd ../ && dpkg-deb --build --root-owner-group -Z xz package ${{ env.LINUX_PACKAGE_NAME }}
- name: Upload Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ${{ env.LINUX_APP_RELEASE_PATH }}/${{ env.LINUX_ZIP_NAME }}
asset_name: ${{ env.LINUX_ZIP_NAME }}
asset_content_type: application/octet-stream
- name: Upload Asset Install Package
id: upload-release-asset-install-package
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: ${{ env.LINUX_APP_RELEASE_PATH }}/${{ env.LINUX_PACKAGE_NAME }}
asset_name: ${{ env.LINUX_PACKAGE_NAME }}
asset_content_type: application/octet-stream
build-for-docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./frontend/scripts/docker-buildfiles/Dockerfile
builder: ${{ steps.buildx.outputs.name }}
push: true
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/appflowy_client:${{ github.ref_name }}
cache-from: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/af_build_cache:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/af_build_cache:buildcache,mode=max
notify-discord:
runs-on: ubuntu-latest
needs: [build-for-linux, build-for-windows, build-for-macOS]
steps:
- name: Notify Discord
run: |
curl -H "Content-Type: application/json" -d '{"username": "release@appflowy", "content": "🎉 AppFlowy ${{ github.ref_name }} is available. https://github.com/LucasXu0/AppFlowy/releases/tag/'${{ github.ref_name }}'"}' "https://discord.com/api/webhooks/${{ secrets.DISCORD }}"
shell: bash
-35
View File
@@ -1,35 +0,0 @@
name: release_docker
on:
push:
branches:
- 'main'
tags:
- '*'
jobs:
build-docker-image:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./frontend/scripts/docker-buildfiles/Dockerfile
builder: ${{ steps.buildx.outputs.name }}
push: true
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/appflowy_client:${{ github.ref_name }}
cache-from: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/af_build_cache:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/af_build_cache:buildcache,mode=max
+86
View File
@@ -0,0 +1,86 @@
name: Rust-CI
on:
push:
branches:
- "main"
- "release/*"
paths:
- "frontend/rust-lib/**"
- "shared-lib/**"
pull_request:
branches:
- "main"
- "release/*"
paths:
- "frontend/rust-lib/**"
- "shared-lib/**"
env:
CARGO_TERM_COLOR: always
RUST_TOOLCHAIN: "1.65"
FLUTTER_VERSION: "3.3.9"
jobs:
test-on-ubuntu:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v2
- name: Install Rust toolchain
id: rust_toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
override: true
components: rustfmt, clippy
profile: minimal
- name: Install flutter
id: flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Install prerequisites
working-directory: frontend
run: |
cargo install --force cargo-make
cargo install --force duckscript_cli
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ matrix.job.os }}
- name: Build FlowySDK
working-directory: frontend
run: |
cargo make --profile development-linux-x86_64 appflowy-core-dev
- name: rustfmt rust-lib
run: cargo fmt --all -- --check
working-directory: frontend/rust-lib/
- name: clippy rust-lib
run: cargo clippy --features="rev-sqlite"
working-directory: frontend/rust-lib
- name: Run rust-lib tests
working-directory: frontend/rust-lib
run: RUST_LOG=info cargo test --no-default-features --features="sync,rev-sqlite"
- name: rustfmt shared-lib
run: cargo fmt --all -- --check
working-directory: shared-lib
- name: clippy shared-lib
run: cargo clippy -- -D warnings
working-directory: shared-lib
- name: Run shared-lib tests
working-directory: shared-lib
run: RUST_LOG=info cargo test --no-default-features
+35 -46
View File
@@ -1,4 +1,4 @@
name: Rust coverage tests
name: Rust code coverage
on:
push:
@@ -9,65 +9,54 @@ on:
- "frontend/rust-lib/**"
- "shared-lib/**"
pull_request:
branches:
- "main"
- "release/*"
paths:
- "frontend/rust-lib/**"
- "shared-lib/**"
env:
CARGO_TERM_COLOR: always
FLUTTER_VERSION: "3.3.9"
RUST_TOOLCHAIN: "1.65"
jobs:
test-coverage:
tests:
runs-on: ubuntu-latest
steps:
- name: Checkout
- name: Checkout source code
uses: actions/checkout@v2
- id: rust_toolchain
- name: Install Rust toolchain
id: rust_toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: 'stable-2022-04-07'
toolchain: ${{ env.RUST_TOOLCHAIN }}
target: ${{ matrix.job.target }}
override: true
profile: minimal
- name: Cache Cargo
uses: actions/cache@v2
with:
path: |
~/.cargo
key: ${{ runner.os }}-cargo-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
- name: Install flutter
id: flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Cache Rust
uses: actions/cache@v2
with:
path: |
frontend/rust-lib/target
shared-lib/target
key: ${{ runner.os }}-rust-rust-lib-share-lib-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
- name: Setup Environment
run: |
if [ "$RUNNER_OS" == "Linux" ]; then
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
sudo apt-get update
sudo apt-get install -y dart curl build-essential libsqlite3-dev libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
sudo apt-get install keybinder-3.0
elif [ "$RUNNER_OS" == "macOS" ]; then
echo 'do nothing'
fi
shell: bash
- name: Install cargo-make, grcov and llvm-tools-preview
- name: Install prerequisites
working-directory: frontend
run: |
cargo install cargo-make
cargo install --force cargo-make
cargo install --force duckscript_cli
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ matrix.job.os }}
- name: Install code-coverage tools
working-directory: frontend
run: |
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
sudo apt-get update
sudo apt-get install keybinder-3.0
cargo install grcov
rustup component add llvm-tools-preview
- name: Run Coverage tests and generate LCOV report
- name: Run tests
working-directory: frontend
run: cargo make get_ci_test_coverage
run: cargo make rust_unit_test_with_coverage
-61
View File
@@ -1,61 +0,0 @@
name: Rust lint
on:
push:
branches:
- "main"
- "release/*"
paths:
- "frontend/rust-lib/**"
- "shared-lib/**"
pull_request:
branches:
- "main"
- "release/*"
paths:
- "frontend/rust-lib/**"
- "shared-lib/**"
env:
CARGO_TERM_COLOR: always
jobs:
rust-fmt:
name: Rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: 'stable-2022-04-07'
override: true
- uses: subosito/flutter-action@v1
with:
flutter-version: '3.0.5'
channel: "stable"
- name: Rust Deps
working-directory: frontend
run: |
cargo install cargo-make
cargo make appflowy-deps-tools
- name: Build FlowySDK
working-directory: frontend
run: |
cargo make --profile development-linux-x86_64 appflowy-sdk-dev
- run: rustup component add rustfmt
working-directory: frontend/rust-lib
- name: rustfmt
run: cargo fmt --all -- --check
working-directory: frontend/rust-lib/
- run: rustup component add clippy
working-directory: frontend/rust-lib
- name: clippy
run: cargo clippy --no-default-features
working-directory: frontend/rust-lib
-60
View File
@@ -1,60 +0,0 @@
name: Backend test
on:
push:
branches:
- "main"
- "release/*"
paths:
- "frontend/rust-lib/**"
- "shared-lib/**"
pull_request:
branches:
- "main"
- "release/*"
paths:
- "frontend/rust-lib/**"
- "shared-lib/**"
env:
CARGO_TERM_COLOR: always
jobs:
tests:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- id: rust_toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: 'stable-2022-04-07'
- name: Cache Cargo
uses: actions/cache@v2
with:
path: |
~/.cargo
key: ${{ runner.os }}-cargo-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
- name: Cache Rust
uses: actions/cache@v2
with:
path: |
frontend/rust-lib/target
shared-lib/target
key: ${{ runner.os }}-rust-rust-lib-share-lib-${{ steps.rust_toolchain.outputs.rustc_hash }}-${{ hashFiles('./frontend/rust-lib/Cargo.toml') }}
- name: Install cargo-make
working-directory: frontend
run: cargo install cargo-make
- name: Run rust-lib tests
working-directory: frontend/rust-lib
run: RUST_LOG=info cargo test --no-default-features --features="sync"
- name: Run shared-lib tests
working-directory: shared-lib
run: RUST_LOG=info cargo test --no-default-features
+82
View File
@@ -0,0 +1,82 @@
name: Tauri-CI
on:
pull_request:
paths:
- "frontend/rust-lib/**"
- "frontend/appflowy_tauri/**"
env:
NODE_VERSION: "16"
RUST_TOOLCHAIN: "1.65"
jobs:
tauri-build:
strategy:
fail-fast: false
matrix:
# platform: [macos-latest, ubuntu-20.04, windows-latest]
platform: [macos-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v3
- name: setup node
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install Rust toolchain
id: rust_toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
override: true
profile: minimal
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ matrix.platform }}
- name: install dependencies (windows only)
if: matrix.platform == 'windows-latest'
working-directory: frontend
run: |
cargo install --force cargo-make
cargo install --force duckscript_cli
vcpkg integrate install
cargo make appflowy-tauri-deps-tools
- name: install dependencies (ubuntu only)
if: matrix.platform == 'ubuntu-20.04'
working-directory: frontend
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
cargo install --force cargo-make
cargo make appflowy-tauri-deps-tools
- name: install dependencies (macOS only)
if: matrix.platform == 'macos-latest'
working-directory: frontend
run: |
cargo install --force cargo-make
cargo make appflowy-tauri-deps-tools
# - name: lint
# working-directory: frontend/appflowy_tauri
# run: |
# yarn --frozen-lockfile
# yarn test:prettier
# yarn test:code
- name: build
working-directory: frontend/appflowy_tauri
run: |
mkdir dist
npm install
cargo make --cwd .. tauri_build
yarn && yarn build
- uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -3,7 +3,7 @@ on:
push:
branches: [ main ]
paths:
- "frontend/app_flowy/assets/translations/en.json"
- "frontend/appflowy_flutter/assets/translations/en.json"
jobs:
Discord-Notify:
+2 -3
View File
@@ -19,10 +19,8 @@ node_modules
**/.cache
**/.DS_Store
**/src/protobuf
**/resources/proto
frontend/.vscode/*
!frontend/.vscode/settings.json
!frontend/.vscode/tasks.json
!frontend/.vscode/launch.json
@@ -31,8 +29,9 @@ frontend/.vscode/*
# Commit the highest level pubspec.lock, but ignore the others
pubspec.lock
!frontend/app_flowy/pubspec.lock
!frontend/appflowy_flutter/pubspec.lock
# ignore tool used for commit linting
.githooks/gitlint
.githooks/gitlint.exe
.fvm/
+1 -1
View File
@@ -10,7 +10,7 @@
<env name="flowy_tool" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/scripts/flowy-tool/Cargo.toml" />
<env name="rust_lib" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/" />
<env name="shared_lib" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/../shared_lib" />
<env name="flutter_lib" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/app_flowy/packages" />
<env name="flutter_lib" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/appflowy_flutter/packages" />
<env name="derive_meta" value="${shared_lib}/flowy-derive/src/derive_cache/derive_cache.rs" />
<env name="flutter_package_lib" value="${flutter_lib}/flowy_sdk/lib" />
</envs>
+1 -1
View File
@@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="dart-event" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="command" value="run --manifest-path $PROJECT_DIR$/scripts/flowy-tool/Cargo.toml -- dart-event --rust_source=$PROJECT_DIR$/rust-lib/ --output=$PROJECT_DIR$/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event.dart" />
<option name="command" value="run --manifest-path $PROJECT_DIR$/scripts/flowy-tool/Cargo.toml -- dart-event --rust_source=$PROJECT_DIR$/rust-lib/ --output=$PROJECT_DIR$/appflowy_flutter/packages/flowy_sdk/lib/dispatch/dart_event.dart" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
<option name="channel" value="DEFAULT" />
<option name="allFeatures" value="false" />
+124 -24
View File
@@ -1,58 +1,150 @@
# Release Notes
## Version 0.0.7 - 11/27/2022
## Version 0.1.2 - 03/28/2023
### Bug Fixes
- Fix: update calendar selected range.
- Fix: duplicate view.
## Version 0.1.1 - 03/21/2023
### New features
- AppFlowy brings the power of OpenAI into your AppFlowy pages. Ask AI to write anything for you in AppFlowy.
- Support adding a cover image to your page, making your pages beautiful.
- More shortcuts become available. Click on '?' at the bottom right to access our shortcut guide.
### Bug Fixes
- Fix some bugs
## Version 0.1.0 - 02/09/2023
### New features
- Support linking a Board or Grid into the Document page
- Integrate a callout plugin implemented by community members
- Optimize user interface
### Bug Fixes
- Fix some bugs
## Version 0.0.9.1 - 01/03/2023
### New features
- New theme
- Support changing text color of editor
- Optimize user interface
### Bug Fixes
- Fix some grid bugs
## Version 0.0.9 - 12/21/2022
### New features
- Enable the user to define where to store their data
- Support inserting Emojis through the slash command
### Bug Fixes
- Fix some bugs
## Version 0.0.8.1 - 12/09/2022
### New features
- Support following your default system theme
- Improve the filter in Grid
### Bug Fixes
- Copy/Paste
## Version 0.0.8 - 11/30/2022
### New features
- Table-view database
- support column type: Checklist
- Board-view database
- support column type: Checklist
- Customize font size: small, medium, large
## Version 0.0.7.1 - 11/30/2022
### Bug Fixes
- Fix some bugs
## Version 0.0.7 - 11/27/2022
### New features
- Support adding filters by the text/checkbox/single-select property in Grid
## Version 0.0.6.2 - 10/30/2022
- Fix some bugs
## Version 0.0.6.1 - 10/26/2022
### New features
- Optimize appflowy_editor dark mode style
### Bug Fixes
- Unable to copy the text with checkbox or link style
## Version 0.0.6 - 10/23/2022
### New features
- Integrate **appflowy_editor**
- Integrate **appflowy_editor**
## Version 0.0.5.3 - 09/26/2022
### New features
- Open the next page automatically after deleting the current page
- Refresh the Kanban board after altering a property type
### Bug Fixes
- Fix switch board bug
- Fix delete the Kanban board's row error
- Remove duplicate time format
- Fix can't delete field in property edit panel
- Adjust some display UI issues
## Version 0.0.5.2 - 09/16/2022
### New features
- Enable adding a new card to the "No Status" group
- Fix some bugs
### Bug Fixes
- Fix cannot open AppFlowy error
- Fix delete the Kanban board's row error
## Version 0.0.5.1 - 09/14/2022
### New features
- Enable deleting a field in board
- Enable deleting a field in board
- Fix some bugs
## Version 0.0.5 - 09/08/2022
### New features - Kanban Board like Notion and Trello beta
Boards are the best way to manage projects & tasks. Use them to group your databases by select, multiselect, and checkbox.
<p align="left"><img src="https://user-images.githubusercontent.com/12026239/190055984-6efa2d7a-ee38-4551-859e-ee56388e1859.gif" width="1000px" /></p>
@@ -62,6 +154,7 @@ Boards are the best way to manage projects & tasks. Use them to group your datab
- Update database properties in the Board view by clicking on a property and making edits on the card
### Other Features & Improvements
- Settings allow users to change avatars
- Click and drag the right edge to resize your sidebar
- And many user interface improvements (link)
@@ -69,27 +162,30 @@ Boards are the best way to manage projects & tasks. Use them to group your datab
## Version 0.0.5 - beta.2 - beta.1 - 09/01/2022
### New features
- Board-view database
- Support start editing after creating a new card
- Support editing the card directly by clicking the edit button
- Add the `No Status` column to display the cards while their status is empty
### Bug Fixes
- Optimize insert card animation
- Fix some UI bugs
## Version 0.0.5 - beta.1 - 08/25/2022
### New features
- Board-view database
- Board-view database
- Group by single select
- drag and drop cards
- insert / delete cards
![Aug-25-2022 16-22-38](https://user-images.githubusercontent.com/86001920/186614248-23186dfe-410e-427a-8cc6-865b1f79e074.gif)
## Version 0.0.4 - 06/06/2022
- Drag to adjust the width of a column
- Upgrade to Flutter 3.0
- Native support for M1 chip
@@ -98,10 +194,11 @@ Boards are the best way to manage projects & tasks. Use them to group your datab
- Keyboard shortcuts support for Grid: press Enter to leave the edit mode; control c/v to copy-paste cell values
### Bug Fixes
- Fixed some bugs
## Version 0.0.4 - beta.3 - 05/02/2022
- Drag to reorder app/ view/ field
- Row record opens as a page
- Auto resize the height of the row in the grid
@@ -110,42 +207,45 @@ Boards are the best way to manage projects & tasks. Use them to group your datab
![May-03-2022 10-03-00](https://user-images.githubusercontent.com/86001920/166394640-a8f1f3bc-5f20-4033-93e9-16bc308d7005.gif)
### Bug Fixes & Improvements
- Improved row/cell data cache
- Fixed some bugs
## Version 0.0.4 - beta.2 - 04/11/2022
- Support properties: Text, Number, Date, Checkbox, Select, Multi-select
- Insert / delete rows
- Add / delete / hide columns
- Edit property
![](https://user-images.githubusercontent.com/12026239/162753644-bf2f4e7a-2367-4d48-87e6-35e244e83a5b.png)
- Support properties: Text, Number, Date, Checkbox, Select, Multi-select
- Insert / delete rows
- Add / delete / hide columns
- Edit property
![](https://user-images.githubusercontent.com/12026239/162753644-bf2f4e7a-2367-4d48-87e6-35e244e83a5b.png)
## Version 0.0.4 - beta.1 - 04/08/2022
v0.0.4 - beta.1 is pre-release
### New features
- Table-view database
- supported column types: Text, Checkbox, Single-select, Multi-select, Numbers
- hide / delete columns
- insert rows
- support column types: Text, Checkbox, Single-select, Multi-select, Numbers
- hide / delete columns
- insert rows
## Version 0.0.3 - 02/23/2022
v0.0.3 is production ready, available on Linux, macOS, and Windows
### New features
- Dark Mode
- Support new languages: French, Italian, Russian, Simplified Chinese, Spanish
- Dark Mode
- Support new languages: French, Italian, Russian, Simplified Chinese, Spanish
- Add Settings: Toggle on Dark Mode; Select a language
- Show device info
- Show device info
- Add tooltip on the toolbar icons
Bug fixes and improvements
- Increased height of action
- CPU performance issue
- CPU performance issue
- Fix potential data parser error
- More foundation work for online collaboration
+58 -54
View File
@@ -6,51 +6,34 @@
"configurations": [
{
// This task builds the Rust and Dart code of AppFlowy.
"name": "AF: Build All",
"name": "AF-desktop: Build All",
"request": "launch",
"program": "./lib/main.dart",
"type": "dart",
"preLaunchTask": "AF: build_flowy_sdk",
"preLaunchTask": "AF: Build Appflowy Core",
"env": {
"RUST_LOG": "info"
"RUST_LOG": "trace",
// "RUST_LOG": "debug"
},
"cwd": "${workspaceRoot}/app_flowy"
},
{
// This task builds the Rust and Dart code of AppFlowy for android.
"name": "AF: Run Android",
"request": "launch",
"program": "./lib/main.dart",
"type": "dart",
"preLaunchTask": "AF: build_mobile_sdk",
"env": {
"RUST_LOG": "info"
},
"cwd": "${workspaceRoot}/app_flowy"
},
{
"name": "AF: Debug Rust",
"request": "attach",
"type": "lldb",
"pid": "${command:pickMyProcess}"
"cwd": "${workspaceRoot}/appflowy_flutter"
},
{
// This task only builds the Dart code of AppFlowy.
"name": "AF: Build Dart Only",
"name": "AF-desktop: Build Dart Only",
"request": "launch",
"program": "./lib/main.dart",
"type": "dart",
"env": {
"RUST_LOG": "debug"
},
"cwd": "${workspaceRoot}/app_flowy"
"cwd": "${workspaceRoot}/appflowy_flutter"
},
{
// This task builds will:
// - call the clean task,
// - rebuild all the generated Files (including freeze and language files)
// - rebuild the the Rust and Dart code of AppFlowy.
"name": "AF: Clean + Rebuild All",
"name": "AF-desktop: Clean + Rebuild All",
"request": "launch",
"program": "./lib/main.dart",
"type": "dart",
@@ -58,14 +41,40 @@
"env": {
"RUST_LOG": "trace"
},
"cwd": "${workspaceRoot}/app_flowy"
"cwd": "${workspaceRoot}/appflowy_flutter"
},
{
"name": "AF-desktop: Debug Rust",
"request": "attach",
"type": "lldb",
"pid": "${command:pickMyProcess}"
},
// {
// "name": "AF-desktop: profile mode",
// "request": "launch",
// "program": "./lib/main.dart",
// "type": "dart",
// "flutterMode": "profile",
// "cwd": "${workspaceRoot}/appflowy_flutter"
// },
{
// This task builds the Rust and Dart code of AppFlowy for android.
"name": "AF-android: Build All",
"request": "launch",
"program": "./lib/main.dart",
"type": "dart",
"preLaunchTask": "AF: build_mobile_sdk",
"env": {
"RUST_LOG": "info"
},
"cwd": "${workspaceRoot}/appflowy_flutter"
},
{
// This task builds will:
// - call the clean task,
// - rebuild all the generated Files (including freeze and language files)
// - rebuild the the Rust and Dart code of AppFlowy.
"name": "AF: Clean + Rebuild All (Android)",
"name": "AF-android: Clean + Rebuild All",
"request": "launch",
"program": "./lib/main.dart",
"type": "dart",
@@ -73,37 +82,32 @@
"env": {
"RUST_LOG": "info"
},
"cwd": "${workspaceRoot}/app_flowy"
"cwd": "${workspaceRoot}/appflowy_flutter"
},
{
"name": "AF: Build All (rustlog: trace)",
// https://tauri.app/v1/guides/debugging/vs-code
"type": "lldb",
"request": "launch",
"program": "./lib/main.dart",
"type": "dart",
"preLaunchTask": "AF: build_flowy_sdk",
"env": {
"RUST_LOG": "trace"
"name": "AF-tauri: Debug backend",
"cargo": {
"args": [
"build",
"--manifest-path=./appflowy_tauri/src-tauri/Cargo.toml",
"--no-default-features"
]
},
"cwd": "${workspaceRoot}/app_flowy"
},
{
"name": "AF: Build All Android (rustlog: trace)",
"request": "launch",
"program": "./lib/main.dart",
"type": "dart",
"preLaunchTask": "AF: build_mobile_sdk",
"env": {
"RUST_LOG": "trace"
},
"cwd": "${workspaceRoot}/app_flowy"
},
{
"name": "AF: app_flowy (profile mode)",
"request": "launch",
"program": "./lib/main.dart",
"type": "dart",
"flutterMode": "profile",
"cwd": "${workspaceRoot}/app_flowy"
"preLaunchTask": "AF: Tauri UI Dev",
"cwd": "${workspaceRoot}/appflowy_tauri/"
},
// {
// "type": "lldb",
// "request": "launch",
// "name": "AF-tauri: Production Debug",
// "cargo": {
// "args": ["build", "--release", "--manifest-path=./appflowy_tauri/src-tauri/Cargo.toml"]
// },
// "preLaunchTask": "AF: Tauri UI Build",
// "cwd": "${workspaceRoot}/appflowy_tauri/"
// },
]
}
+12 -1
View File
@@ -9,14 +9,25 @@
"editor.tabCompletion": "onlySnippets",
"editor.wordBasedSuggestions": false,
},
"[javascript]": {
"editor.formatOnSave": true,
"editor.rulers": [80],
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.formatOnSave": true,
"editor.rulers": [80],
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"svgviewer.enableautopreview": true,
"svgviewer.previewcolumn": "Active",
"svgviewer.showzoominout": true,
"editor.wordWrapColumn": 80,
"editor.minimap.maxColumn": 140,
"prettier.printWidth": 140,
"editor.wordWrap": "wordWrapColumn",
"dart.lineLength": 80,
"typescript.validate.enable": true,
"javascript.validate.enable": true,
"files.associations": {
"*.log.*": "log"
},
+76 -43
View File
@@ -14,13 +14,13 @@
"type": "shell",
"dependsOrder": "sequence",
"dependsOn": [
"AF: Rust Clean",
"AF: Dart Clean",
"AF: Flutter Clean",
"AF: build_flowy_sdk",
"AF: Build Appflowy Core",
"AF: Flutter Pub Get",
"AF: Flutter Package Get",
"AF: Generate Language Files",
"AF: Generate Freezed Files",
"AF: Generate Freezed Files"
],
"presentation": {
"reveal": "always",
@@ -32,60 +32,39 @@
"type": "shell",
"dependsOrder": "sequence",
"dependsOn": [
"AF: Rust Clean",
"AF: Dart Clean",
"AF: Flutter Clean",
"AF: build_flowy_sdk_for_android",
"AF: Build Appflowy Core_for_android",
"AF: Flutter Pub Get",
"AF: Flutter Package Get",
"AF: Generate Language Files",
"AF: Generate Freezed Files",
"AF: Generate Freezed Files"
],
"presentation": {
"reveal": "always",
"panel": "new",
},
"panel": "new"
}
},
{
"label": "AF: build_flowy_sdk_for_android",
"label": "AF: Build Appflowy Core_for_android",
"type": "shell",
"command": "cargo make --profile development-android appflowy-sdk-dev-android",
"command": "cargo make --profile development-android appflowy-core-dev-android",
"group": "build",
"options": {
"cwd": "${workspaceFolder}"
}
},
{
"label": "AF: build_flowy_sdk",
"label": "AF: Build Appflowy Core",
"type": "shell",
"command": "sh ./scripts/build_sdk.sh",
"windows": {
"options": {
"env": {
"FLOWY_DEV_ENV": "Windows"
},
"shell": {
"executable": "cmd.exe",
"args": [
"/d",
"/c",
".\\scripts\\build_sdk.cmd"
]
}
}
"command": "cargo make --profile development-windows-x86 appflowy-core-dev"
},
"linux": {
"options": {
"env": {
"FLOWY_DEV_ENV": "Linux"
}
}
"command": "cargo make --profile \"development-linux-$(uname -m)\" appflowy-core-dev"
},
"osx": {
"options": {
"env": {
"FLOWY_DEV_ENV": "macOS"
}
}
"command": "cargo make --profile \"development-mac-$(uname -m)\" appflowy-core-dev"
},
"group": "build",
"options": {
@@ -117,7 +96,7 @@
"type": "shell",
"command": "flutter clean",
"options": {
"cwd": "${workspaceFolder}/app_flowy"
"cwd": "${workspaceFolder}/appflowy_flutter"
}
},
{
@@ -125,7 +104,7 @@
"type": "shell",
"command": "flutter pub get",
"options": {
"cwd": "${workspaceFolder}/app_flowy"
"cwd": "${workspaceFolder}/appflowy_flutter"
}
},
{
@@ -133,7 +112,7 @@
"type": "shell",
"command": "flutter packages pub get",
"options": {
"cwd": "${workspaceFolder}/app_flowy"
"cwd": "${workspaceFolder}/appflowy_flutter"
}
},
{
@@ -141,7 +120,7 @@
"type": "shell",
"command": "flutter pub run build_runner build --delete-conflicting-outputs",
"options": {
"cwd": "${workspaceFolder}/app_flowy"
"cwd": "${workspaceFolder}/appflowy_flutter"
}
},
{
@@ -166,9 +145,9 @@
}
},
{
"label": "AF: Rust Clean",
"label": "AF: Flutter Clean",
"type": "shell",
"command": "cargo make flowy_clean",
"command": "cargo make flutter_clean",
"group": "build",
"options": {
"cwd": "${workspaceFolder}"
@@ -184,7 +163,61 @@
],
"group": "build",
"problemMatcher": [],
"detail": "app_flowy"
}
"detail": "appflowy_flutter"
},
{
"label": "AF: Tauri UI Dev",
"type": "shell",
"isBackground": true,
"command": "yarn",
"args": ["dev"],
"options": {
"cwd": "${workspaceFolder}/appflowy_tauri"
}
},
{
"label": "AF: Tauri UI Build",
"type": "shell",
"command": "npm run build",
"options": {
"cwd": "${workspaceFolder}/appflowy_tauri"
}
},
{
"label": "AF: Tauri Dev",
"type": "shell",
"command": "npm run tauri:dev",
"options": {
"cwd": "${workspaceFolder}/appflowy_tauri"
}
},
{
"label": "AF: Tauri Clean",
"type": "shell",
"command": "cargo make tauri_clean",
"options": {
"cwd": "${workspaceFolder}"
}
},
{
"label": "AF: Tauri Clean + Dev",
"type": "shell",
"dependsOrder": "sequence",
"dependsOn": [
"AF: Tauri Clean",
"AF: Tauri UI Dev"
],
"options": {
"cwd": "${workspaceFolder}"
}
},
{
"label": "AF: Tauri ESLint",
"type": "shell",
"command": "npx eslint --fix src",
"options": {
"cwd": "${workspaceFolder}/appflowy_tauri"
}
},
]
}
+22 -22
View File
@@ -1,13 +1,14 @@
#https://github.com/sagiegurari/cargo-make
extend = [
{ path = "scripts/makefile/desktop.toml" },
{ path = "scripts/makefile/protobuf.toml" },
{ path = "scripts/makefile/tests.toml" },
{ path = "scripts/makefile/docker.toml" },
{ path = "scripts/makefile/env.toml" },
{ path = "scripts/makefile/flutter.toml" },
{ path = "scripts/makefile/tool.toml" },
{ path = "scripts/makefile/desktop.toml" },
{ path = "scripts/makefile/protobuf.toml" },
{ path = "scripts/makefile/tests.toml" },
{ path = "scripts/makefile/docker.toml" },
{ path = "scripts/makefile/env.toml" },
{ path = "scripts/makefile/flutter.toml" },
{ path = "scripts/makefile/tool.toml" },
{ path = "scripts/makefile/tauri.toml" },
]
[config]
@@ -22,12 +23,12 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
CARGO_MAKE_CRATE_NAME = "dart-ffi"
LIB_NAME = "dart_ffi"
CURRENT_APP_VERSION = "0.0.7"
FEATURES = "flutter"
CURRENT_APP_VERSION = "0.1.2"
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite"
PRODUCT_NAME = "AppFlowy"
# CRATE_TYPE: https://doc.rust-lang.org/reference/linkage.html
# If you update the macOS's CRATE_TYPE, don't forget to update the
# flowy_sdk.podspec
# appflowy_backend.podspec
# for staticlib:
# s.static_framework = true
# s.vendored_libraries = "libdart_ffi.a"
@@ -42,12 +43,11 @@ PRODUCT_NAME = "AppFlowy"
CRATE_TYPE = "staticlib"
LIB_EXT = "a"
APP_ENVIRONMENT = "local"
FLUTTER_FLOWY_SDK_PATH = "app_flowy/packages/flowy_sdk"
PROTOBUF_DERIVE_CACHE = "../shared-lib/flowy-derive/src/derive_cache/derive_cache.rs"
FLUTTER_FLOWY_SDK_PATH = "appflowy_flutter/packages/appflowy_backend"
TAURI_BACKEND_SERVICE_PATH = "appflowy_tauri/src/services/backend"
# Test default config
TEST_CRATE_TYPE = "cdylib"
TEST_LIB_EXT = "dylib"
TEST_RUST_LOG = "info"
TEST_BUILD_FLAG = "debug"
TEST_COMPILE_TARGET = "x86_64-apple-darwin"
@@ -133,6 +133,7 @@ CRATE_TYPE = "cdylib"
FLUTTER_OUTPUT_DIR = "Debug"
LIB_EXT = "so"
LINUX_ARCH = "arm64"
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
[env.production-linux-aarch64]
BUILD_FLAG = "release"
@@ -143,6 +144,7 @@ FLUTTER_OUTPUT_DIR = "Release"
LIB_EXT = "so"
LINUX_ARCH = "arm64"
APP_ENVIRONMENT = "production"
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
[tasks.echo_env]
script = ['''
@@ -151,7 +153,7 @@ script = ['''
echo BUILD_FLAG: ${BUILD_FLAG}
echo TARGET_OS: ${TARGET_OS}
echo RUST_COMPILE_TARGET: ${RUST_COMPILE_TARGET}
echo FEATURES: ${FEATURES}
echo FEATURES: ${FLUTTER_DESKTOP_FEATURES}
echo PRODUCT_EXT: ${PRODUCT_EXT}
echo APP_ENVIRONMENT: ${APP_ENVIRONMENT}
echo ${platforms}
@@ -170,19 +172,19 @@ BUILD_FLAG = "debug"
TARGET_OS = "android"
CRATE_TYPE = "cdylib"
FLUTTER_OUTPUT_DIR = "Debug"
FEATURES = "flutter,openssl_vendored"
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
[env.production-android]
BUILD_FLAG = "release"
TARGET_OS = "android"
CRATE_TYPE = "cdylib"
FLUTTER_OUTPUT_DIR = "Release"
FEATURES = "flutter,openssl_vendored"
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
[tasks.setup-crate-type]
private = true
script = [
"""
"""
toml = readfile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml
val = replace ${toml} "staticlib" ${CRATE_TYPE}
result = writefile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml ${val}
@@ -194,7 +196,7 @@ script_runner = "@duckscript"
[tasks.restore-crate-type]
private = true
script = [
"""
"""
toml = readfile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml
val = replace ${toml} ${CRATE_TYPE} "staticlib"
result = writefile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml ${val}
@@ -222,7 +224,7 @@ TEST_COMPILE_TARGET = "x86_64-pc-windows-msvc"
[tasks.setup-test-crate-type]
private = true
script = [
"""
"""
toml = readfile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml
val = replace ${toml} "staticlib" ${TEST_CRATE_TYPE}
result = writefile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml ${val}
@@ -234,7 +236,7 @@ script_runner = "@duckscript"
[tasks.restore-test-crate-type]
private = true
script = [
"""
"""
toml = readfile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml
val = replace ${toml} ${TEST_CRATE_TYPE} "staticlib"
result = writefile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml ${val}
@@ -250,5 +252,3 @@ script = ["""
cargo build -vv --features=dart
"""]
script_runner = "@shell"
-21
View File
@@ -1,21 +0,0 @@
# app_flowy
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
## release check
1. [entitlements](https://flutter.dev/desktop#setting-up-entitlements)
2. [symbols stripped](https://flutter.dev/docs/development/platform-integration/c-interop)
@@ -1,236 +0,0 @@
{
"appName": "AppFlowy",
"defaultUsername": "Me",
"welcomeText": "@:appName 에 오신것을 환영합니다",
"githubStarText": "Star on GitHub",
"subscribeNewsletterText": "뉴스레터 구독",
"letsGoButtonText": "Let's Go",
"title": "제목",
"signUp": {
"buttonText": "회원가입",
"title": "@:appName 에 회원가입",
"getStartedText": "시작하기",
"emptyPasswordError": "비밀번호는 공백일 수 없습니다",
"repeatPasswordEmptyError": "비밀번호 재입력란은 공백일 수 없습니다",
"unmatchedPasswordError": "재입력 하신 비밀번호가 같지 않습니다",
"alreadyHaveAnAccount": "이미 계정이 있으신가요?",
"emailHint": "이메일",
"passwordHint": "비밀번호",
"repeatPasswordHint": "비밀번호 재입력"
},
"signIn": {
"loginTitle": "@:appName 에 로그인",
"loginButtonText": "로그인",
"buttonText": "로그인",
"forgotPassword": "비밀번호를 잊으셨나요?",
"emailHint": "이메일",
"passwordHint": "비밀번호",
"dontHaveAnAccount": "계정이 없으신가요?",
"repeatPasswordEmptyError": "비밀번호 재입력란은 공백일 수 없습니다",
"unmatchedPasswordError": "재입력 하신 비밀번호가 같지 않습니다"
},
"workspace": {
"create": "워크스페이스 생성",
"hint": "워크스페이스",
"notFoundError": "워크스페이스를 찾을 수 없습니다"
},
"shareAction": {
"buttonText": "공유",
"workInProgress": "Coming soon",
"markdown": "마크다운",
"copyLink": "링크 복사"
},
"disclosureAction": {
"rename": "이름변경",
"delete": "삭제",
"duplicate": "복제"
},
"blankPageTitle": "빈 페이지",
"newPageText": "새로운 페이지",
"trash": {
"text": "휴지통",
"restoreAll": "모두 복구",
"deleteAll": "모두 삭제",
"pageHeader": {
"fileName": "파일 이름",
"lastModified": "수정날짜",
"created": "생성날짜"
}
},
"deletePagePrompt": {
"text": "현재 페이지는 휴지통에 있습니다",
"restore": "페이지 복구",
"deletePermanent": "영구 삭제"
},
"dialogCreatePageNameHint": "페이지 이름",
"questionBubble": {
"whatsNew": "새로운 소식",
"help": "도움 및 지원",
"debug": {
"name": "디버그 정보",
"success": "디버그 정보를 클립보드로 복사했습니다.",
"fail": "디버그 정보를 클립보드로 복사할 수 없습니다."
}
},
"menuAppHeader": {
"addPageTooltip": "하위에 페이지 추가",
"defaultNewPageName": "제목없음",
"renameDialog": "이름변경"
},
"toolbar": {
"undo": "실행취소",
"redo": "재실행",
"bold": "굵게",
"italic": "기울임꼴",
"underline": "밑줄",
"strike": "취소선",
"numList": "번호 매기기 목록",
"bulletList": "글머리 기호 목록",
"checkList": "작업 목록",
"inlineCode": "인라인 코드",
"quote": "인용구 블록",
"header": "헤더",
"highlight": "하이라이트"
},
"tooltip": {
"lightMode": "라이트 모드로 변경",
"darkMode": "다크 모드로 변경",
"openAsPage": "페이지로 열기",
"addNewRow": "열 추가",
"openMenu": "메뉴를 여시려면 클릭하세요"
},
"sideBar": {
"closeSidebar": "사이드바 닫기",
"openSidebar": "사이드바 열기"
},
"notifications": {
"export": {
"markdown": "마크다운으로 노트를 내보냄",
"path": "Documents/flowy"
}
},
"contactsPage": {
"title": "연락처",
"whatsHappening": "이번주에는 무슨 일이 있나요?",
"addContact": "연락처 추가",
"editContact": "연락처 편집"
},
"button": {
"OK": "확인",
"Cancel": "취소",
"signIn": "로그인",
"signOut": "로그아웃",
"complete": "완료",
"save": "저장"
},
"label": {
"welcome": "환영합니다!",
"firstName": "이름",
"middleName": "중간 이름",
"lastName": "성",
"stepX": "{X} 단계"
},
"oAuth": {
"err": {
"failedTitle": "계정에 연결을 할 수 없습니다.",
"failedMsg": "브라우저에서 회원가입이 완료되었는지 확인해주세요."
},
"google": {
"title": "GOOGLE SIGN-IN",
"instruction1": "구글 연락처를 가져오기 위해서 웹브라우저로 앱을 승인 해야 합니다.",
"instruction2": "아이콘을 클릭 또는 텍스트를 선택해서 이 코드를 클립보드로 복사하세요:",
"instruction3": "웹브라우저로 다음 링크로 가셔서 위 코드를 입력해주세요:",
"instruction4": "가입 완료 후 아래 버튼을 눌러주세요:"
}
},
"settings": {
"title": "설정",
"menu": {
"appearance": "화면",
"language": "언어",
"user": "사용자",
"open": "설정 열기"
},
"appearance": {
"lightLabel": "라이트 모드",
"darkLabel": "다크 모드"
}
},
"grid": {
"settings": {
"filter": "필터",
"sortBy": "정렬 기준",
"Properties": "속성",
"group": "그룹"
},
"field": {
"hide": "숨기기",
"insertLeft": "왼쪽 삽입",
"insertRight": "오른쪽 삽입",
"duplicate": "복제",
"delete": "삭제",
"textFieldName": "텍스트",
"checkboxFieldName": "체크박스",
"dateFieldName": "날짜",
"numberFieldName": "숫자",
"singleSelectFieldName": "선택",
"multiSelectFieldName": "다중선택",
"urlFieldName": "링크",
"numberFormat": " 숫자 형식",
"dateFormat": " 날짜 형식",
"includeTime": " 시간 표시",
"dateFormatFriendly": "월 일,년",
"dateFormatISO": "년-월-일",
"dateFormatLocal": "월/일/년",
"dateFormatUS": "년/월/일",
"timeFormat": " 시간 형식",
"invalidTimeFormat": "잘못된 형식",
"timeFormatTwelveHour": "12 시간",
"timeFormatTwentyFourHour": "24 시간",
"addSelectOption": "옵션 추가",
"optionTitle": "옵션",
"addOption": "옵션 추가",
"editProperty": "속성 편집",
"newColumn": "열 추가",
"deleteFieldPromptMessage": "해당 속성을 삭제 하시겠습니까?"
},
"row": {
"duplicate": "복제",
"delete": "삭제",
"textPlaceholder": "비어있음",
"copyProperty": "속성이 클립보드로 복사됨",
"count": "개수",
"newRow": "행 추가"
},
"selectOption": {
"create": "생성",
"purpleColor": "보라색",
"pinkColor": "핑크색",
"lightPinkColor": "연한 핑크색",
"orangeColor": "오렌지색",
"yellowColor": "노랑색",
"limeColor": "라임색",
"greenColor": "초록색",
"aquaColor": "아쿠아색",
"blueColor": "파랑색",
"deleteTag": "태그 삭제",
"colorPanelTitle": "색상",
"panelTitle": "옵션 선택 또는 생성",
"searchOption": "옵션 검색"
},
"menuName": "그리드"
},
"document": {
"menuName": "도큐먼트",
"date": {
"timeHintTextInTwelveHour": "01:00 PM",
"timeHintTextInTwentyFourHour": "13:00"
}
},
"board": {
"column": {
"create_new_card": "추가"
}
}
}
@@ -1,233 +0,0 @@
{
"appName": "AppFlowy",
"defaultUsername": "Я",
"welcomeText": "Добро пожаловать в @:appName",
"githubStarText": "Поставить звезду на GitHub",
"subscribeNewsletterText": "Подписаться на рассылку",
"letsGoButtonText": "Начнём",
"title": "Заголовок",
"signUp": {
"buttonText": "Зарегистрироваться",
"title": "Регистрация в @:appName",
"getStartedText": "Начать",
"emptyPasswordError": "Пароль не может быть пустым",
"repeatPasswordEmptyError": "Повтор пароля не может быть пустым",
"unmatchedPasswordError": "Пароли не совпадают",
"alreadyHaveAnAccount": "Уже есть аккаунт?",
"emailHint": "Электронная почта",
"passwordHint": "Пароль",
"repeatPasswordHint": "Повторите пароль"
},
"signIn": {
"loginTitle": "Войти в @:appName",
"loginButtonText": "Войти",
"buttonText": "Авторизация",
"forgotPassword": "Забыли пароль?",
"emailHint": "Электронная почта",
"passwordHint": "Пароль",
"dontHaveAnAccount": "Нет аккаунта?",
"repeatPasswordEmptyError": "Повтор пароля не может быть пустым",
"unmatchedPasswordError": "Пароли не совпадают"
},
"workspace": {
"create": "Создать рабочее пространство",
"hint": "рабочее пространство",
"notFoundError": "Нет такого рабочего пространства"
},
"shareAction": {
"buttonText": "Поделиться",
"workInProgress": "В разработке",
"markdown": "Markdown",
"copyLink": "Скопировать ссылку"
},
"disclosureAction": {
"rename": "Переименовать",
"delete": "Удалить",
"duplicate": "Дублировать"
},
"blankPageTitle": "Пустая страница",
"newPageText": "Новая страница",
"trash": {
"text": "Корзина",
"restoreAll": "Восстановить всё",
"deleteAll": "Очистить",
"pageHeader": {
"fileName": "Имя",
"lastModified": "Последнее изменение",
"created": "Создан"
}
},
"deletePagePrompt": {
"text": "Эта страница в Корзине",
"restore": "Восстановить страницу",
"deletePermanent": "Удалить навсегда"
},
"dialogCreatePageNameHint": "Имя",
"questionBubble": {
"whatsNew": "Что нового?",
"help": "Помощь",
"debug": {
"name": "Отладочная информация",
"success": "Скопировано в буфер обмена!",
"fail": "Не получилось скопировать"
}
},
"menuAppHeader": {
"addPageTooltip": "Быстро добавить новую страницу",
"defaultNewPageName": "Без заголовка",
"renameDialog": "Переименовать"
},
"toolbar": {
"undo": "Отменить",
"redo": "Повторить",
"bold": "Жирный",
"italic": "Курсив",
"underline": "Подчёркнутый",
"strike": "Зачёркнутый",
"numList": "Нумерованный список",
"bulletList": "Маркированный список",
"checkList": "Список To-Do",
"inlineCode": "Код",
"quote": "Цитата",
"header": "Заголовок",
"highlight": "Выделение"
},
"tooltip": {
"darkMode": "Переключиться в тёмную тему",
"openAsPage": "Открыть как страницу",
"addNewRow": "Добавить новую строку",
"openMenu": "Открыть меню"
},
"sideBar": {
"closeSidebar": "Закрыть боковое меню",
"openSidebar": "Открыть боковое меню"
},
"notifications": {
"export": {
"markdown": "Заметка экспортирована в Markdown",
"path": "Документы/flowy"
}
},
"contactsPage": {
"title": "Контакты",
"whatsHappening": "Что происходит на этой неделе?",
"addContact": "Новый контакт",
"editContact": "Редактировать"
},
"button": {
"OK": "OK",
"Cancel": "Отмена",
"signIn": "Войти",
"signOut": "Выйти",
"complete": "Завершить",
"save": "Сохранить"
},
"label": {
"welcome": "Добро пожаловать!",
"firstName": "Имя",
"middleName": "Отчество",
"lastName": "Фамилия",
"stepX": "Этап {X}"
},
"oAuth": {
"err": {
"failedTitle": "Ошибка подключения к аккаунту.",
"failedMsg": "Убедитесь, что вы завершили вход в своём браузере."
},
"google": {
"title": "Вход через Google",
"instruction1": "Чтобы импортировать ваши Google Контакты, вам нужно будет авторизовать приложение через браузер.",
"instruction2": "Скопируйте этот код в буфер обмена (нажав кнопку или выделив текст):",
"instruction3": "Пройдите по ссылке и введите этот код:",
"instruction4": "Нажмите на кнопку ниже, когда завершите вход:"
}
},
"settings": {
"title": "Настройки",
"menu": {
"appearance": "Внешний вид",
"language": "Язык",
"user": "Пользователь",
"open": "Открыть настройки"
},
"appearance": {
"lightLabel": "Светлая",
"darkLabel": "Тёмная"
}
},
"grid": {
"settings": {
"filter": "Фильтр",
"sortBy": "Сортировать",
"Properties": "Свойства",
"group": "Группировать"
},
"field": {
"hide": "Скрыть",
"insertLeft": "Вставить слева",
"insertRight": "Вставить справа",
"duplicate": "Дублировать",
"delete": "Удалить",
"textFieldName": "Текст",
"checkboxFieldName": "Чекбокс",
"dateFieldName": "Дата",
"numberFieldName": "Число",
"singleSelectFieldName": "Выбор",
"multiSelectFieldName": "Выбор нескольких",
"urlFieldName": "URL",
"numberFormat": " Формат числа",
"dateFormat": " Формат даты",
"includeTime": " Время",
"dateFormatFriendly": "День Месяц, Год",
"dateFormatISO": "Год-Месяц-День",
"dateFormatLocal": "Месяц/День/Год",
"dateFormatUS": "Год/Месяц/День",
"timeFormat": " Форматировать время",
"invalidTimeFormat": "Неверный формат",
"timeFormatTwelveHour": "12 часов",
"timeFormatTwentyFourHour": "24 часа",
"addSelectOption": "Добавить вариант",
"optionTitle": "Варианты",
"addOption": "Добавить",
"editProperty": "Редактировать свойство",
"newColumn": "Добавить колонку",
"deleteFieldPromptMessage": "Вы уверены? Свойство будет удалено"
},
"row": {
"duplicate": "Дублировать",
"delete": "Удалить",
"textPlaceholder": "Пусто",
"copyProperty": "Свойство скопировано",
"count": "Количество"
},
"selectOption": {
"create": "Создать",
"purpleColor": "Фиолетовый",
"pinkColor": "Розовый",
"lightPinkColor": "Светло-розовый",
"orangeColor": "Оранжевый",
"yellowColor": "Желтый",
"limeColor": "Ярко-зелёный",
"greenColor": "Зелёный",
"aquaColor": "Бирюзовый",
"blueColor": "Синий",
"deleteTag": "Удалить вариант",
"colorPanelTitle": "Цвета",
"panelTitle": "Выберите или создайте вариант",
"searchOption": "Поиск"
},
"menuName": "Сетка"
},
"document": {
"menuName": "Документ",
"date": {
"timeHintTextInTwelveHour": "01:00 PM",
"timeHintTextInTwentyFourHour": "13:00"
}
},
"board": {
"column": {
"create_new_card": "Создать"
}
}
}
@@ -1,39 +0,0 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/dart_notification.pb.dart';
import 'package:flowy_sdk/rust_stream.dart';
import 'notification_helper.dart';
// Folder
typedef FolderNotificationCallback = void Function(FolderNotification, Either<Uint8List, FlowyError>);
class FolderNotificationParser extends NotificationParser<FolderNotification, FlowyError> {
FolderNotificationParser({String? id, required FolderNotificationCallback callback})
: super(
id: id,
callback: callback,
tyParser: (ty) => FolderNotification.valueOf(ty),
errorParser: (bytes) => FlowyError.fromBuffer(bytes),
);
}
typedef FolderNotificationHandler = Function(FolderNotification ty, Either<Uint8List, FlowyError> result);
class FolderNotificationListener {
StreamSubscription<SubscribeObject>? _subscription;
FolderNotificationParser? _parser;
FolderNotificationListener({required String objectId, required FolderNotificationHandler handler})
: _parser = FolderNotificationParser(id: objectId, callback: handler) {
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable));
}
Future<void> stop() async {
_parser = null;
await _subscription?.cancel();
}
}
@@ -1,46 +0,0 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
import 'package:flowy_sdk/rust_stream.dart';
import 'notification_helper.dart';
// GridPB
typedef GridNotificationCallback = void Function(
GridDartNotification, Either<Uint8List, FlowyError>);
class GridNotificationParser
extends NotificationParser<GridDartNotification, FlowyError> {
GridNotificationParser(
{String? id, required GridNotificationCallback callback})
: super(
id: id,
callback: callback,
tyParser: (ty) => GridDartNotification.valueOf(ty),
errorParser: (bytes) => FlowyError.fromBuffer(bytes),
);
}
typedef GridNotificationHandler = Function(
GridDartNotification ty, Either<Uint8List, FlowyError> result);
class GridNotificationListener {
StreamSubscription<SubscribeObject>? _subscription;
GridNotificationParser? _parser;
GridNotificationListener(
{required String objectId, required GridNotificationHandler handler})
: _parser = GridNotificationParser(id: objectId, callback: handler) {
_subscription =
RustStreamReceiver.listen((observable) => _parser?.parse(observable));
}
Future<void> stop() async {
_parser = null;
await _subscription?.cancel();
_subscription = null;
}
}
@@ -1,39 +0,0 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.dart';
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/rust_stream.dart';
import 'notification_helper.dart';
// User
typedef UserNotificationCallback = void Function(UserNotification, Either<Uint8List, FlowyError>);
class UserNotificationParser extends NotificationParser<UserNotification, FlowyError> {
UserNotificationParser({required String id, required UserNotificationCallback callback})
: super(
id: id,
callback: callback,
tyParser: (ty) => UserNotification.valueOf(ty),
errorParser: (bytes) => FlowyError.fromBuffer(bytes),
);
}
typedef UserNotificationHandler = Function(UserNotification ty, Either<Uint8List, FlowyError> result);
class UserNotificationListener {
StreamSubscription<SubscribeObject>? _subscription;
UserNotificationParser? _parser;
UserNotificationListener({required String objectId, required UserNotificationHandler handler})
: _parser = UserNotificationParser(id: objectId, callback: handler) {
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable));
}
Future<void> stop() async {
_parser = null;
await _subscription?.cancel();
}
}
-22
View File
@@ -1,22 +0,0 @@
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/user/presentation/splash_screen.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:hotkey_manager/hotkey_manager.dart';
import 'package:flutter/material.dart';
class FlowyApp implements EntryPoint {
@override
Widget create() {
return const SplashScreen();
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized();
WidgetsFlutterBinding.ensureInitialized();
await hotKeyManager.unregisterAll();
await FlowyRunner.run(FlowyApp());
}
@@ -1,171 +0,0 @@
import 'dart:collection';
import 'package:app_flowy/plugins/grid/application/block/block_cache.dart';
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
import 'package:app_flowy/plugins/grid/application/grid_service.dart';
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'dart:async';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
import 'board_listener.dart';
typedef OnFieldsChanged = void Function(UnmodifiableListView<FieldInfo>);
typedef OnGridChanged = void Function(GridPB);
typedef DidLoadGroups = void Function(List<GroupPB>);
typedef OnUpdatedGroup = void Function(List<GroupPB>);
typedef OnDeletedGroup = void Function(List<String>);
typedef OnInsertedGroup = void Function(List<InsertedGroupPB>);
typedef OnResetGroups = void Function(List<GroupPB>);
typedef OnRowsChanged = void Function(
List<RowInfo>,
RowsChangedReason,
);
typedef OnError = void Function(FlowyError);
class BoardDataController {
final String gridId;
final GridFFIService _gridFFIService;
final GridFieldController fieldController;
final BoardListener _listener;
// key: the block id
final LinkedHashMap<String, GridBlockCache> _blocks;
UnmodifiableMapView<String, GridBlockCache> get blocks =>
UnmodifiableMapView(_blocks);
OnFieldsChanged? _onFieldsChanged;
OnGridChanged? _onGridChanged;
DidLoadGroups? _didLoadGroup;
OnRowsChanged? _onRowsChanged;
OnError? _onError;
List<RowInfo> get rowInfos {
final List<RowInfo> rows = [];
for (var block in _blocks.values) {
rows.addAll(block.rows);
}
return rows;
}
BoardDataController({required ViewPB view})
: gridId = view.id,
_listener = BoardListener(view.id),
// ignore: prefer_collection_literals
_blocks = LinkedHashMap(),
_gridFFIService = GridFFIService(gridId: view.id),
fieldController = GridFieldController(gridId: view.id);
void addListener({
required OnGridChanged onGridChanged,
OnFieldsChanged? onFieldsChanged,
required DidLoadGroups didLoadGroups,
OnRowsChanged? onRowsChanged,
required OnUpdatedGroup onUpdatedGroup,
required OnDeletedGroup onDeletedGroup,
required OnInsertedGroup onInsertedGroup,
required OnResetGroups onResetGroups,
required OnError? onError,
}) {
_onGridChanged = onGridChanged;
_onFieldsChanged = onFieldsChanged;
_didLoadGroup = didLoadGroups;
_onRowsChanged = onRowsChanged;
_onError = onError;
fieldController.addListener(onFields: (fields) {
_onFieldsChanged?.call(UnmodifiableListView(fields));
});
_listener.start(
onBoardChanged: (result) {
result.fold(
(changeset) {
if (changeset.updateGroups.isNotEmpty) {
onUpdatedGroup.call(changeset.updateGroups);
}
if (changeset.deletedGroups.isNotEmpty) {
onDeletedGroup.call(changeset.deletedGroups);
}
if (changeset.insertedGroups.isNotEmpty) {
onInsertedGroup.call(changeset.insertedGroups);
}
},
(e) => _onError?.call(e),
);
},
onGroupByNewField: (result) {
result.fold(
(groups) => onResetGroups(groups),
(e) => _onError?.call(e),
);
},
);
}
Future<Either<Unit, FlowyError>> openGrid() async {
final result = await _gridFFIService.openGrid();
return Future(
() => result.fold(
(grid) async {
_onGridChanged?.call(grid);
final result = await fieldController.loadFields(
fieldIds: grid.fields,
);
return result.fold(
(l) {
_loadGroups(grid.blocks);
return left(l);
},
(err) => right(err),
);
},
(err) => right(err),
),
);
}
Future<Either<RowPB, FlowyError>> createBoardCard(String groupId,
{String? startRowId}) {
return _gridFFIService.createBoardCard(groupId, startRowId);
}
Future<void> dispose() async {
await _gridFFIService.closeGrid();
await fieldController.dispose();
for (final blockCache in _blocks.values) {
blockCache.dispose();
}
}
Future<void> _loadGroups(List<BlockPB> blocks) async {
for (final block in blocks) {
final cache = GridBlockCache(
gridId: gridId,
block: block,
fieldController: fieldController,
);
cache.addListener(onRowsChanged: (reason) {
_onRowsChanged?.call(rowInfos, reason);
});
_blocks[block.id] = cache;
}
final result = await _gridFFIService.loadGroups();
return Future(
() => result.fold(
(groups) {
_didLoadGroup?.call(groups.items);
},
(err) => _onError?.call(err),
),
);
}
}
@@ -1,66 +0,0 @@
import 'dart:typed_data';
import 'package:app_flowy/core/grid_notification.dart';
import 'package:flowy_infra/notifier.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/group_changeset.pb.dart';
typedef GroupUpdateValue = Either<GroupViewChangesetPB, FlowyError>;
typedef GroupByNewFieldValue = Either<List<GroupPB>, FlowyError>;
class BoardListener {
final String viewId;
PublishNotifier<GroupUpdateValue>? _groupUpdateNotifier = PublishNotifier();
PublishNotifier<GroupByNewFieldValue>? _groupByNewFieldNotifier =
PublishNotifier();
GridNotificationListener? _listener;
BoardListener(this.viewId);
void start({
required void Function(GroupUpdateValue) onBoardChanged,
required void Function(GroupByNewFieldValue) onGroupByNewField,
}) {
_groupUpdateNotifier?.addPublishListener(onBoardChanged);
_groupByNewFieldNotifier?.addPublishListener(onGroupByNewField);
_listener = GridNotificationListener(
objectId: viewId,
handler: _handler,
);
}
void _handler(
GridDartNotification ty,
Either<Uint8List, FlowyError> result,
) {
switch (ty) {
case GridDartNotification.DidUpdateGroupView:
result.fold(
(payload) => _groupUpdateNotifier?.value =
left(GroupViewChangesetPB.fromBuffer(payload)),
(error) => _groupUpdateNotifier?.value = right(error),
);
break;
case GridDartNotification.DidGroupByNewField:
result.fold(
(payload) => _groupByNewFieldNotifier?.value =
left(GroupViewChangesetPB.fromBuffer(payload).newGroups),
(error) => _groupByNewFieldNotifier?.value = right(error),
);
break;
default:
break;
}
}
Future<void> stop() async {
await _listener?.stop();
_groupUpdateNotifier?.dispose();
_groupUpdateNotifier = null;
_groupByNewFieldNotifier?.dispose();
_groupByNewFieldNotifier = null;
}
}
@@ -1,49 +0,0 @@
import 'package:app_flowy/plugins/board/presentation/card/card_cell_builder.dart';
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_field_notifier.dart';
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
import 'package:flutter/foundation.dart';
typedef OnCardChanged = void Function(GridCellMap, RowsChangedReason);
class CardDataController extends BoardCellBuilderDelegate {
final RowPB rowPB;
final GridFieldController _fieldController;
final GridRowCache _rowCache;
final List<VoidCallback> _onCardChangedListeners = [];
CardDataController({
required this.rowPB,
required GridFieldController fieldController,
required GridRowCache rowCache,
}) : _fieldController = fieldController,
_rowCache = rowCache;
GridCellMap loadData() {
return _rowCache.loadGridCells(rowPB.id);
}
void addListener({OnCardChanged? onRowChanged}) {
_onCardChangedListeners.add(_rowCache.addListener(
rowId: rowPB.id,
onCellUpdated: onRowChanged,
));
}
void dispose() {
for (final fn in _onCardChangedListeners) {
_rowCache.removeRowListener(fn);
}
}
@override
GridCellFieldNotifier buildFieldNotifier() {
return GridCellFieldNotifier(
notifier: GridCellFieldNotifierImpl(_fieldController));
}
@override
GridCellCache get cellCache => _rowCache.cellCache;
}
@@ -1,20 +0,0 @@
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
class BoardGroupService {
final String gridId;
FieldPB? groupField;
BoardGroupService(this.gridId);
void setGroupField(FieldPB field) {
groupField = field;
}
}
abstract class CanBeGroupField {
String get groupContent;
}
// class SingleSelectGroup extends CanBeGroupField {
// final SingleSelectTypeOptionContext typeOptionContext;
// }
@@ -1,51 +0,0 @@
import 'dart:typed_data';
import 'package:app_flowy/core/grid_notification.dart';
import 'package:flowy_infra/notifier.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/group_changeset.pb.dart';
typedef UpdateGroupNotifiedValue = Either<GroupRowsNotificationPB, FlowyError>;
class GroupListener {
final GroupPB group;
PublishNotifier<UpdateGroupNotifiedValue>? _groupNotifier = PublishNotifier();
GridNotificationListener? _listener;
GroupListener(this.group);
void start({
required void Function(UpdateGroupNotifiedValue) onGroupChanged,
}) {
_groupNotifier?.addPublishListener(onGroupChanged);
_listener = GridNotificationListener(
objectId: group.groupId,
handler: _handler,
);
}
void _handler(
GridDartNotification ty,
Either<Uint8List, FlowyError> result,
) {
switch (ty) {
case GridDartNotification.DidUpdateGroup:
result.fold(
(payload) => _groupNotifier?.value =
left(GroupRowsNotificationPB.fromBuffer(payload)),
(error) => _groupNotifier?.value = right(error),
);
break;
default:
break;
}
}
Future<void> stop() async {
await _listener?.stop();
_groupNotifier?.dispose();
_groupNotifier = null;
}
}
@@ -1,83 +0,0 @@
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter/material.dart';
import 'board_cell.dart';
import 'board_checkbox_cell.dart';
import 'board_date_cell.dart';
import 'board_number_cell.dart';
import 'board_select_option_cell.dart';
import 'board_text_cell.dart';
import 'board_url_cell.dart';
abstract class BoardCellBuilderDelegate
extends GridCellControllerBuilderDelegate {
GridCellCache get cellCache;
}
class BoardCellBuilder {
final BoardCellBuilderDelegate delegate;
BoardCellBuilder(this.delegate);
Widget buildCell(
String groupId,
GridCellIdentifier cellId,
EditableCellNotifier cellNotifier,
) {
final cellControllerBuilder = GridCellControllerBuilder(
delegate: delegate,
cellId: cellId,
cellCache: delegate.cellCache,
);
final key = cellId.key();
switch (cellId.fieldType) {
case FieldType.Checkbox:
return BoardCheckboxCell(
groupId: groupId,
cellControllerBuilder: cellControllerBuilder,
key: key,
);
case FieldType.DateTime:
return BoardDateCell(
groupId: groupId,
cellControllerBuilder: cellControllerBuilder,
key: key,
);
case FieldType.SingleSelect:
return BoardSelectOptionCell(
groupId: groupId,
cellControllerBuilder: cellControllerBuilder,
key: key,
);
case FieldType.MultiSelect:
return BoardSelectOptionCell(
groupId: groupId,
cellControllerBuilder: cellControllerBuilder,
editableNotifier: cellNotifier,
key: key,
);
case FieldType.Number:
return BoardNumberCell(
groupId: groupId,
cellControllerBuilder: cellControllerBuilder,
key: key,
);
case FieldType.RichText:
return BoardTextCell(
groupId: groupId,
cellControllerBuilder: cellControllerBuilder,
editableNotifier: cellNotifier,
key: key,
);
case FieldType.URL:
return BoardUrlCell(
groupId: groupId,
cellControllerBuilder: cellControllerBuilder,
key: key,
);
}
throw UnimplementedError;
}
}
@@ -1,126 +0,0 @@
import 'package:app_flowy/plugins/document/editor_styles.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/horizontal_rule_node_widget.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/plugins/document/presentation/banner.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import 'application/doc_bloc.dart';
class DocumentPage extends StatefulWidget {
final VoidCallback onDeleted;
final ViewPB view;
DocumentPage({
required this.view,
required this.onDeleted,
Key? key,
}) : super(key: ValueKey(view.id));
@override
State<DocumentPage> createState() => _DocumentPageState();
}
class _DocumentPageState extends State<DocumentPage> {
late DocumentBloc documentBloc;
final FocusNode _focusNode = FocusNode();
@override
void initState() {
// The appflowy editor use Intl as localization, set the default language as fallback.
Intl.defaultLocale = 'en_US';
documentBloc = getIt<DocumentBloc>(param1: super.widget.view)
..add(const DocumentEvent.initial());
super.initState();
}
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<DocumentBloc>.value(value: documentBloc),
],
child:
BlocBuilder<DocumentBloc, DocumentState>(builder: (context, state) {
return state.loadingState.map(
loading: (_) => SizedBox.expand(
child: Container(color: Colors.transparent),
),
finish: (result) => result.successOrFail.fold(
(_) {
if (state.forceClose) {
widget.onDeleted();
return const SizedBox();
} else {
return _renderDocument(context, state);
}
},
(err) => FlowyErrorPage(err.toString()),
),
);
}),
);
}
@override
Future<void> dispose() async {
documentBloc.close();
_focusNode.dispose();
super.dispose();
}
Widget _renderDocument(BuildContext context, DocumentState state) {
return Column(
children: [
if (state.isDeleted) _renderBanner(context),
// AppFlowy Editor
_renderAppFlowyEditor(
context.read<DocumentBloc>().editorState,
),
],
);
}
Widget _renderBanner(BuildContext context) {
return DocumentBanner(
onRestore: () =>
context.read<DocumentBloc>().add(const DocumentEvent.restorePage()),
onDelete: () => context
.read<DocumentBloc>()
.add(const DocumentEvent.deletePermanently()),
);
}
Widget _renderAppFlowyEditor(EditorState editorState) {
final theme = Theme.of(context);
final editorMaxWidth = MediaQuery.of(context).size.width * 0.6;
final editor = AppFlowyEditor(
editorState: editorState,
autoFocus: editorState.document.isEmpty,
customBuilders: {
'horizontal_rule': HorizontalRuleWidgetBuilder(),
},
shortcutEvents: [
insertHorizontalRule,
],
themeData: theme.copyWith(extensions: [
...theme.extensions.values,
customEditorTheme(context),
...customPluginTheme(context),
]),
);
return Expanded(
child: Center(
child: Container(
constraints: BoxConstraints(
maxWidth: editorMaxWidth,
),
child: editor,
),
),
);
}
}
@@ -1,56 +0,0 @@
import 'dart:async';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
import '../field/field_controller.dart';
import '../row/row_cache.dart';
import 'block_listener.dart';
/// Read https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/grid for more information
class GridBlockCache {
final String gridId;
final BlockPB block;
late GridRowCache _rowCache;
late GridBlockListener _listener;
List<RowInfo> get rows => _rowCache.visibleRows;
GridRowCache get rowCache => _rowCache;
GridBlockCache({
required this.gridId,
required this.block,
required GridFieldController fieldController,
}) {
_rowCache = GridRowCache(
gridId: gridId,
block: block,
notifier: GridRowFieldNotifierImpl(fieldController),
);
_listener = GridBlockListener(blockId: block.id);
_listener.start((result) {
result.fold(
(changeset) => _rowCache.applyChangesets(changeset),
(err) => Log.error(err),
);
});
}
Future<void> dispose() async {
await _listener.stop();
await _rowCache.dispose();
}
void addListener({
required void Function(RowsChangedReason) onRowsChanged,
bool Function()? listenWhen,
}) {
_rowCache.onRowsChanged((reason) {
if (listenWhen != null && listenWhen() == false) {
return;
}
onRowsChanged(reason);
});
}
}
@@ -1,53 +0,0 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:app_flowy/core/grid_notification.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_infra/notifier.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
typedef GridBlockUpdateNotifierValue = Either<GridBlockChangesetPB, FlowyError>;
class GridBlockListener {
final String blockId;
PublishNotifier<GridBlockUpdateNotifierValue>? _rowsUpdateNotifier =
PublishNotifier();
GridNotificationListener? _listener;
GridBlockListener({required this.blockId});
void start(void Function(GridBlockUpdateNotifierValue) onBlockChanged) {
if (_listener != null) {
_listener?.stop();
}
_listener = GridNotificationListener(
objectId: blockId,
handler: _handler,
);
_rowsUpdateNotifier?.addPublishListener(onBlockChanged);
}
void _handler(GridDartNotification ty, Either<Uint8List, FlowyError> result) {
switch (ty) {
case GridDartNotification.DidUpdateGridBlock:
result.fold(
(payload) => _rowsUpdateNotifier?.value =
left(GridBlockChangesetPB.fromBuffer(payload)),
(error) => _rowsUpdateNotifier?.value = right(error),
);
break;
default:
break;
}
}
Future<void> stop() async {
await _listener?.stop();
_rowsUpdateNotifier?.dispose();
_rowsUpdateNotifier = null;
}
}
@@ -1,342 +0,0 @@
part of 'cell_service.dart';
typedef GridCellController = IGridCellController<String, String>;
typedef GridCheckboxCellController = IGridCellController<String, String>;
typedef GridNumberCellController = IGridCellController<String, String>;
typedef GridSelectOptionCellController
= IGridCellController<SelectOptionCellDataPB, String>;
typedef GridDateCellController
= IGridCellController<DateCellDataPB, CalendarData>;
typedef GridURLCellController = IGridCellController<URLCellDataPB, String>;
abstract class GridCellControllerBuilderDelegate {
GridCellFieldNotifier buildFieldNotifier();
}
class GridCellControllerBuilder {
final GridCellIdentifier _cellId;
final GridCellCache _cellCache;
final GridCellControllerBuilderDelegate delegate;
GridCellControllerBuilder({
required this.delegate,
required GridCellIdentifier cellId,
required GridCellCache cellCache,
}) : _cellCache = cellCache,
_cellId = cellId;
IGridCellController build() {
final cellFieldNotifier = delegate.buildFieldNotifier();
switch (_cellId.fieldType) {
case FieldType.Checkbox:
final cellDataLoader = GridCellDataLoader(
cellId: _cellId,
parser: StringCellDataParser(),
);
return GridCellController(
cellId: _cellId,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(cellId: _cellId),
);
case FieldType.DateTime:
final cellDataLoader = GridCellDataLoader(
cellId: _cellId,
parser: DateCellDataParser(),
reloadOnFieldChanged: true,
);
return GridDateCellController(
cellId: _cellId,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
fieldNotifier: cellFieldNotifier,
cellDataPersistence: DateCellDataPersistence(cellId: _cellId),
);
case FieldType.Number:
final cellDataLoader = GridCellDataLoader(
cellId: _cellId,
parser: StringCellDataParser(),
reloadOnFieldChanged: true,
);
return GridNumberCellController(
cellId: _cellId,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(cellId: _cellId),
);
case FieldType.RichText:
final cellDataLoader = GridCellDataLoader(
cellId: _cellId,
parser: StringCellDataParser(),
);
return GridCellController(
cellId: _cellId,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(cellId: _cellId),
);
case FieldType.MultiSelect:
case FieldType.SingleSelect:
final cellDataLoader = GridCellDataLoader(
cellId: _cellId,
parser: SelectOptionCellDataParser(),
reloadOnFieldChanged: true,
);
return GridSelectOptionCellController(
cellId: _cellId,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(cellId: _cellId),
);
case FieldType.URL:
final cellDataLoader = GridCellDataLoader(
cellId: _cellId,
parser: URLCellDataParser(),
);
return GridURLCellController(
cellId: _cellId,
cellCache: _cellCache,
cellDataLoader: cellDataLoader,
fieldNotifier: cellFieldNotifier,
cellDataPersistence: CellDataPersistence(cellId: _cellId),
);
}
throw UnimplementedError;
}
}
/// IGridCellController is used to manipulate the cell and receive notifications.
/// * Read/Write cell data
/// * Listen on field/cell notifications.
///
/// Generic T represents the type of the cell data.
/// Generic D represents the type of data that will be saved to the disk
///
// ignore: must_be_immutable
class IGridCellController<T, D> extends Equatable {
final GridCellIdentifier cellId;
final GridCellCache _cellsCache;
final GridCellCacheKey _cacheKey;
final FieldService _fieldService;
final GridCellFieldNotifier _fieldNotifier;
final GridCellDataLoader<T> _cellDataLoader;
final IGridCellDataPersistence<D> _cellDataPersistence;
CellListener? _cellListener;
ValueNotifier<T?>? _cellDataNotifier;
bool isListening = false;
VoidCallback? _onFieldChangedFn;
Timer? _loadDataOperation;
Timer? _saveDataOperation;
bool _isDispose = false;
IGridCellController({
required this.cellId,
required GridCellCache cellCache,
required GridCellFieldNotifier fieldNotifier,
required GridCellDataLoader<T> cellDataLoader,
required IGridCellDataPersistence<D> cellDataPersistence,
}) : _cellsCache = cellCache,
_cellDataLoader = cellDataLoader,
_cellDataPersistence = cellDataPersistence,
_fieldNotifier = fieldNotifier,
_fieldService = FieldService(
gridId: cellId.gridId,
fieldId: cellId.fieldInfo.id,
),
_cacheKey = GridCellCacheKey(
rowId: cellId.rowId,
fieldId: cellId.fieldInfo.id,
);
String get gridId => cellId.gridId;
String get rowId => cellId.rowId;
String get fieldId => cellId.fieldInfo.id;
FieldInfo get fieldInfo => cellId.fieldInfo;
FieldType get fieldType => cellId.fieldInfo.fieldType;
VoidCallback? startListening({
required void Function(T?) onCellChanged,
VoidCallback? onCellFieldChanged,
}) {
if (isListening) {
Log.error("Already started. It seems like you should call clone first");
return null;
}
isListening = true;
_cellDataNotifier = ValueNotifier(_cellsCache.get(_cacheKey));
_cellListener =
CellListener(rowId: cellId.rowId, fieldId: cellId.fieldInfo.id);
/// 1.Listen on user edit event and load the new cell data if needed.
/// For example:
/// user input: 12
/// cell display: $12
_cellListener?.start(onCellChanged: (result) {
result.fold(
(_) {
_cellsCache.remove(_cacheKey);
_loadData();
},
(err) => Log.error(err),
);
});
/// 2.Listen on the field event and load the cell data if needed.
_onFieldChangedFn = () {
if (onCellFieldChanged != null) {
onCellFieldChanged();
}
/// reloadOnFieldChanged should be true if you need to load the data when the corresponding field is changed
/// For example:
/// ¥12 -> $12
if (_cellDataLoader.reloadOnFieldChanged) {
_loadData();
}
};
_fieldNotifier.register(_cacheKey, _onFieldChangedFn!);
/// Notify the listener, the cell data was changed.
onCellChangedFn() => onCellChanged(_cellDataNotifier?.value);
_cellDataNotifier?.addListener(onCellChangedFn);
// Return the function pointer that can be used when calling removeListener.
return onCellChangedFn;
}
void removeListener(VoidCallback fn) {
_cellDataNotifier?.removeListener(fn);
}
/// Return the cell data.
/// The cell data will be read from the Cache first, and load from disk if it does not exist.
/// You can set [loadIfNotExist] to false (default is true) to disable loading the cell data.
T? getCellData({bool loadIfNotExist = true}) {
final data = _cellsCache.get(_cacheKey);
if (data == null && loadIfNotExist) {
_loadData();
}
return data;
}
/// Return the TypeOptionPB that can be parsed into corresponding class using the [parser].
/// [PD] is the type that the parser return.
Future<Either<PD, FlowyError>>
getFieldTypeOption<PD, P extends TypeOptionDataParser>(P parser) {
return _fieldService
.getFieldTypeOptionData(fieldType: fieldType)
.then((result) {
return result.fold(
(data) => left(parser.fromBuffer(data.typeOptionData)),
(err) => right(err),
);
});
}
/// Save the cell data to disk
/// You can set [deduplicate] to true (default is false) to reduce the save operation.
/// It's useful when you call this method when user editing the [TextField].
/// The default debounce interval is 300 milliseconds.
void saveCellData(D data,
{bool deduplicate = false,
void Function(Option<FlowyError>)? resultCallback}) async {
if (deduplicate) {
_loadDataOperation?.cancel();
_saveDataOperation?.cancel();
_saveDataOperation = Timer(const Duration(milliseconds: 300), () async {
final result = await _cellDataPersistence.save(data);
if (resultCallback != null) {
resultCallback(result);
}
});
} else {
final result = await _cellDataPersistence.save(data);
if (resultCallback != null) {
resultCallback(result);
}
}
}
void _loadData() {
_saveDataOperation?.cancel();
_loadDataOperation?.cancel();
_loadDataOperation = Timer(const Duration(milliseconds: 10), () {
_cellDataLoader.loadData().then((data) {
if (data != null) {
_cellsCache.insert(_cacheKey, GridCell(object: data));
} else {
_cellsCache.remove(_cacheKey);
}
_cellDataNotifier?.value = data;
});
});
}
Future<void> dispose() async {
if (_isDispose) {
Log.error("$this should only dispose once");
return;
}
_isDispose = true;
await _cellListener?.stop();
_loadDataOperation?.cancel();
_saveDataOperation?.cancel();
_cellDataNotifier = null;
if (_onFieldChangedFn != null) {
_fieldNotifier.unregister(_cacheKey, _onFieldChangedFn!);
await _fieldNotifier.dispose();
_onFieldChangedFn = null;
}
}
@override
List<Object> get props =>
[_cellsCache.get(_cacheKey) ?? "", cellId.rowId + cellId.fieldInfo.id];
}
class GridCellFieldNotifierImpl extends IGridCellFieldNotifier {
final GridFieldController _fieldController;
OnReceiveUpdateFields? _onChangesetFn;
GridCellFieldNotifierImpl(GridFieldController cache)
: _fieldController = cache;
@override
void onCellDispose() {
if (_onChangesetFn != null) {
_fieldController.removeListener(onChangesetListener: _onChangesetFn!);
_onChangesetFn = null;
}
}
@override
void onCellFieldChanged(void Function(FieldInfo) callback) {
_onChangesetFn = (List<FieldInfo> filedInfos) {
for (final field in filedInfos) {
callback(field);
}
};
_fieldController.addListener(
onFieldsUpdated: _onChangesetFn,
);
}
}
@@ -1,64 +0,0 @@
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
import 'package:flutter/foundation.dart';
import 'cell_service.dart';
abstract class IGridCellFieldNotifier {
void onCellFieldChanged(void Function(FieldInfo) callback);
void onCellDispose();
}
/// GridPB's cell helper wrapper that enables each cell will get notified when the corresponding field was changed.
/// You Register an onFieldChanged callback to listen to the cell changes, and unregister if you don't want to listen.
class GridCellFieldNotifier {
final IGridCellFieldNotifier notifier;
/// fieldId: {objectId: callback}
final Map<String, Map<String, List<VoidCallback>>> _fieldListenerByFieldId =
{};
GridCellFieldNotifier({required this.notifier}) {
notifier.onCellFieldChanged(
(field) {
final map = _fieldListenerByFieldId[field.id];
if (map != null) {
for (final callbacks in map.values) {
for (final callback in callbacks) {
callback();
}
}
}
},
);
}
///
void register(GridCellCacheKey cacheKey, VoidCallback onFieldChanged) {
var map = _fieldListenerByFieldId[cacheKey.fieldId];
if (map == null) {
_fieldListenerByFieldId[cacheKey.fieldId] = {};
map = _fieldListenerByFieldId[cacheKey.fieldId];
map![cacheKey.rowId] = [onFieldChanged];
} else {
var objects = map[cacheKey.rowId];
if (objects == null) {
map[cacheKey.rowId] = [onFieldChanged];
} else {
objects.add(onFieldChanged);
}
}
}
void unregister(GridCellCacheKey cacheKey, VoidCallback fn) {
var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.rowId];
final index = callbacks?.indexWhere((callback) => callback == fn);
if (index != null && index != -1) {
callbacks?.removeAt(index);
}
}
Future<void> dispose() async {
notifier.onCellDispose();
_fieldListenerByFieldId.clear();
}
}
@@ -1,76 +0,0 @@
import 'dart:async';
import 'dart:collection';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option_entities.pb.dart';
import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:app_flowy/plugins/grid/application/cell/cell_listener.dart';
import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
import 'dart:convert' show utf8;
import '../../field/field_controller.dart';
import '../../field/type_option/type_option_context.dart';
import 'cell_field_notifier.dart';
part 'cell_service.freezed.dart';
part 'cell_data_loader.dart';
part 'cell_controller.dart';
part 'cell_cache.dart';
part 'cell_data_persistence.dart';
// key: rowId
class CellService {
CellService();
Future<Either<void, FlowyError>> updateCell({
required GridCellIdentifier cellId,
required String data,
}) {
final payload = CellChangesetPB.create()
..gridId = cellId.gridId
..fieldId = cellId.fieldId
..rowId = cellId.rowId
..content = data;
return GridEventUpdateCell(payload).send();
}
Future<Either<CellPB, FlowyError>> getCell({
required GridCellIdentifier cellId,
}) {
final payload = CellPathPB.create()
..gridId = cellId.gridId
..fieldId = cellId.fieldId
..rowId = cellId.rowId;
return GridEventGetCell(payload).send();
}
}
/// Id of the cell
/// We can locate the cell by using gridId + rowId + field.id.
@freezed
class GridCellIdentifier with _$GridCellIdentifier {
const factory GridCellIdentifier({
required String gridId,
required String rowId,
required FieldInfo fieldInfo,
}) = _GridCellIdentifier;
// ignore: unused_element
const GridCellIdentifier._();
String get fieldId => fieldInfo.id;
FieldType get fieldType => fieldInfo.fieldType;
ValueKey key() {
return ValueKey("$rowId$fieldId${fieldInfo.fieldType}");
}
}
@@ -1,88 +0,0 @@
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_service.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
import 'cell_service/cell_service.dart';
class SelectOptionService {
final GridCellIdentifier cellId;
SelectOptionService({required this.cellId});
String get gridId => cellId.gridId;
String get fieldId => cellId.fieldInfo.id;
String get rowId => cellId.rowId;
Future<Either<Unit, FlowyError>> create({required String name}) {
return TypeOptionFFIService(gridId: gridId, fieldId: fieldId)
.newOption(name: name)
.then(
(result) {
return result.fold(
(option) {
final cellIdentifier = CellPathPB.create()
..gridId = gridId
..fieldId = fieldId
..rowId = rowId;
final payload = SelectOptionChangesetPB.create()
..insertOptions.add(option)
..cellIdentifier = cellIdentifier;
return GridEventUpdateSelectOption(payload).send();
},
(r) => right(r),
);
},
);
}
Future<Either<Unit, FlowyError>> update({
required SelectOptionPB option,
}) {
final payload = SelectOptionChangesetPB.create()
..updateOptions.add(option)
..cellIdentifier = _cellIdentifier();
return GridEventUpdateSelectOption(payload).send();
}
Future<Either<Unit, FlowyError>> delete(
{required Iterable<SelectOptionPB> options}) {
final payload = SelectOptionChangesetPB.create()
..deleteOptions.addAll(options)
..cellIdentifier = _cellIdentifier();
return GridEventUpdateSelectOption(payload).send();
}
Future<Either<SelectOptionCellDataPB, FlowyError>> getOptionContext() {
final payload = CellPathPB.create()
..gridId = gridId
..fieldId = fieldId
..rowId = rowId;
return GridEventGetSelectOptionCellData(payload).send();
}
Future<Either<void, FlowyError>> select(
{required Iterable<String> optionIds}) {
final payload = SelectOptionCellChangesetPB.create()
..cellIdentifier = _cellIdentifier()
..insertOptionIds.addAll(optionIds);
return GridEventUpdateSelectOptionCell(payload).send();
}
Future<Either<void, FlowyError>> unSelect(
{required Iterable<String> optionIds}) {
final payload = SelectOptionCellChangesetPB.create()
..cellIdentifier = _cellIdentifier()
..deleteOptionIds.addAll(optionIds);
return GridEventUpdateSelectOptionCell(payload).send();
}
CellPathPB _cellIdentifier() {
return CellPathPB.create()
..gridId = gridId
..fieldId = fieldId
..rowId = rowId;
}
}
@@ -1,542 +0,0 @@
import 'dart:collection';
import 'package:app_flowy/plugins/grid/application/field/grid_listener.dart';
import 'package:app_flowy/plugins/grid/application/filter/filter_listener.dart';
import 'package:app_flowy/plugins/grid/application/filter/filter_service.dart';
import 'package:app_flowy/plugins/grid/application/grid_service.dart';
import 'package:app_flowy/plugins/grid/application/setting/setting_listener.dart';
import 'package:app_flowy/plugins/grid/application/setting/setting_service.dart';
import 'package:app_flowy/plugins/grid/presentation/widgets/filter/filter_info.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/setting_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/util.pb.dart';
import 'package:flutter/foundation.dart';
import '../row/row_cache.dart';
class _GridFieldNotifier extends ChangeNotifier {
List<FieldInfo> _fieldInfos = [];
set fieldInfos(List<FieldInfo> fieldInfos) {
_fieldInfos = fieldInfos;
notifyListeners();
}
void notify() {
notifyListeners();
}
List<FieldInfo> get fieldInfos => _fieldInfos;
}
class _GridFilterNotifier extends ChangeNotifier {
List<FilterInfo> _filters = [];
set filters(List<FilterInfo> filters) {
_filters = filters;
notifyListeners();
}
void notify() {
notifyListeners();
}
List<FilterInfo> get filters => _filters;
}
typedef OnReceiveUpdateFields = void Function(List<FieldInfo>);
typedef OnReceiveFields = void Function(List<FieldInfo>);
typedef OnReceiveFilters = void Function(List<FilterInfo>);
class GridFieldController {
final String gridId;
// Listeners
final GridFieldsListener _fieldListener;
final SettingListener _settingListener;
final FiltersListener _filterListener;
// FFI services
final GridFFIService _gridFFIService;
final SettingFFIService _settingFFIService;
final FilterFFIService _filterFFIService;
// Field callbacks
final Map<OnReceiveFields, VoidCallback> _fieldCallbacks = {};
_GridFieldNotifier? _fieldNotifier = _GridFieldNotifier();
// Field updated callbacks
final Map<OnReceiveUpdateFields, void Function(List<FieldInfo>)>
_updatedFieldCallbacks = {};
// Group callbacks
final Map<String, GroupConfigurationPB> _groupConfigurationByFieldId = {};
// Filter callbacks
final Map<OnReceiveFilters, VoidCallback> _filterCallbacks = {};
_GridFilterNotifier? _filterNotifier = _GridFilterNotifier();
final Map<String, FilterPB> _filterPBByFieldId = {};
// Getters
List<FieldInfo> get fieldInfos => [..._fieldNotifier?.fieldInfos ?? []];
List<FilterInfo> get filterInfos => [..._filterNotifier?.filters ?? []];
FieldInfo? getField(String fieldId) {
final fields = _fieldNotifier?.fieldInfos
.where((element) => element.id == fieldId)
.toList() ??
[];
if (fields.isEmpty) {
return null;
}
assert(fields.length == 1);
return fields.first;
}
FilterInfo? getFilter(String filterId) {
final filters = _filterNotifier?.filters
.where((element) => element.filter.id == filterId)
.toList() ??
[];
if (filters.isEmpty) {
return null;
}
assert(filters.length == 1);
return filters.first;
}
GridFieldController({required this.gridId})
: _fieldListener = GridFieldsListener(gridId: gridId),
_settingListener = SettingListener(gridId: gridId),
_filterListener = FiltersListener(viewId: gridId),
_gridFFIService = GridFFIService(gridId: gridId),
_filterFFIService = FilterFFIService(viewId: gridId),
_settingFFIService = SettingFFIService(viewId: gridId) {
//Listen on field's changes
_listenOnFieldChanges();
//Listen on setting changes
_listenOnSettingChanges();
//Listen on the fitler changes
_listenOnFilterChanges();
_settingFFIService.getSetting().then((result) {
result.fold(
(setting) => _updateSettingConfiguration(setting),
(err) => Log.error(err),
);
});
}
void _listenOnFilterChanges() {
//Listen on the fitler changes
_filterListener.start(onFilterChanged: (result) {
result.fold(
(changeset) {
final List<FilterInfo> filters = filterInfos;
// Deletes the filters
final deleteFilterIds =
changeset.deleteFilters.map((e) => e.id).toList();
if (deleteFilterIds.isNotEmpty) {
filters.retainWhere(
(element) => !deleteFilterIds.contains(element.filter.id),
);
_filterPBByFieldId.removeWhere(
(key, value) => deleteFilterIds.contains(value.id));
}
// Inserts the new filter if it's not exist
for (final newFilter in changeset.insertFilters) {
final filterIndex = filters
.indexWhere((element) => element.filter.id == newFilter.id);
if (filterIndex == -1) {
final fieldInfo = _findFieldInfoForFilter(fieldInfos, newFilter);
if (fieldInfo != null) {
_filterPBByFieldId[fieldInfo.id] = newFilter;
filters.add(FilterInfo(gridId, newFilter, fieldInfo));
}
}
}
for (final updatedFilter in changeset.updateFilters) {
final filterIndex = filters.indexWhere(
(element) => element.filter.id == updatedFilter.filterId,
);
// Remove the old filter
if (filterIndex != -1) {
filters.removeAt(filterIndex);
_filterPBByFieldId.removeWhere(
(key, value) => value.id == updatedFilter.filterId);
}
// Insert the filter if there is a fitler and its field info is
// not null
if (updatedFilter.hasFilter()) {
final fieldInfo = _findFieldInfoForFilter(
fieldInfos,
updatedFilter.filter,
);
if (fieldInfo != null) {
// Insert the filter with the position: filterIndex, otherwise,
// append it to the end of the list.
final filterInfo =
FilterInfo(gridId, updatedFilter.filter, fieldInfo);
if (filterIndex != -1) {
filters.insert(filterIndex, filterInfo);
} else {
filters.add(filterInfo);
}
_filterPBByFieldId[fieldInfo.id] = updatedFilter.filter;
}
}
}
_updateFieldInfos();
_filterNotifier?.filters = filters;
},
(err) => Log.error(err),
);
});
}
void _listenOnSettingChanges() {
//Listen on setting changes
_settingListener.start(onSettingUpdated: (result) {
result.fold(
(setting) => _updateSettingConfiguration(setting),
(r) => Log.error(r),
);
});
}
void _listenOnFieldChanges() {
//Listen on field's changes
_fieldListener.start(onFieldsChanged: (result) {
result.fold(
(changeset) {
_deleteFields(changeset.deletedFields);
_insertFields(changeset.insertedFields);
final updateFields = _updateFields(changeset.updatedFields);
for (final listener in _updatedFieldCallbacks.values) {
listener(updateFields);
}
},
(err) => Log.error(err),
);
});
}
void _updateSettingConfiguration(GridSettingPB setting) {
_groupConfigurationByFieldId.clear();
for (final configuration in setting.groupConfigurations.items) {
_groupConfigurationByFieldId[configuration.fieldId] = configuration;
}
for (final configuration in setting.filters.items) {
_filterPBByFieldId[configuration.fieldId] = configuration;
}
_updateFieldInfos();
}
void _updateFieldInfos() {
if (_fieldNotifier != null) {
for (var field in _fieldNotifier!.fieldInfos) {
field._isGroupField = _groupConfigurationByFieldId[field.id] != null;
field._hasFilter = _filterPBByFieldId[field.id] != null;
}
_fieldNotifier?.notify();
}
}
Future<void> dispose() async {
await _fieldListener.stop();
await _filterListener.stop();
await _settingListener.stop();
for (final callback in _fieldCallbacks.values) {
_fieldNotifier?.removeListener(callback);
}
_fieldNotifier?.dispose();
_fieldNotifier = null;
for (final callback in _filterCallbacks.values) {
_filterNotifier?.removeListener(callback);
}
_filterNotifier?.dispose();
_filterNotifier = null;
}
Future<Either<Unit, FlowyError>> loadFields({
required List<FieldIdPB> fieldIds,
}) async {
final result = await _gridFFIService.getFields(fieldIds: fieldIds);
return Future(
() => result.fold(
(newFields) {
_fieldNotifier?.fieldInfos =
newFields.map((field) => FieldInfo(field: field)).toList();
_loadFilters();
_updateFieldInfos();
return left(unit);
},
(err) => right(err),
),
);
}
Future<Either<Unit, FlowyError>> _loadFilters() async {
return _filterFFIService.getAllFilters().then((result) {
return result.fold(
(filterPBs) {
final List<FilterInfo> filters = [];
for (final filterPB in filterPBs) {
final fieldInfo = _findFieldInfoForFilter(fieldInfos, filterPB);
if (fieldInfo != null) {
final filterInfo = FilterInfo(gridId, filterPB, fieldInfo);
filters.add(filterInfo);
}
}
_updateFieldInfos();
_filterNotifier?.filters = filters;
return left(unit);
},
(err) => right(err),
);
});
}
void addListener({
OnReceiveFields? onFields,
OnReceiveUpdateFields? onFieldsUpdated,
OnReceiveFilters? onFilters,
bool Function()? listenWhen,
}) {
if (onFieldsUpdated != null) {
callback(List<FieldInfo> updateFields) {
if (listenWhen != null && listenWhen() == false) {
return;
}
onFieldsUpdated(updateFields);
}
_updatedFieldCallbacks[onFieldsUpdated] = callback;
}
if (onFields != null) {
callback() {
if (listenWhen != null && listenWhen() == false) {
return;
}
onFields(fieldInfos);
}
_fieldCallbacks[onFields] = callback;
_fieldNotifier?.addListener(callback);
}
if (onFilters != null) {
callback() {
if (listenWhen != null && listenWhen() == false) {
return;
}
onFilters(filterInfos);
}
_filterCallbacks[onFilters] = callback;
_filterNotifier?.addListener(callback);
}
}
void removeListener({
OnReceiveFields? onFieldsListener,
OnReceiveFilters? onFiltersListener,
OnReceiveUpdateFields? onChangesetListener,
}) {
if (onFieldsListener != null) {
final callback = _fieldCallbacks.remove(onFieldsListener);
if (callback != null) {
_fieldNotifier?.removeListener(callback);
}
}
if (onFiltersListener != null) {
final callback = _filterCallbacks.remove(onFiltersListener);
if (callback != null) {
_filterNotifier?.removeListener(callback);
}
}
}
void _deleteFields(List<FieldIdPB> deletedFields) {
if (deletedFields.isEmpty) {
return;
}
final List<FieldInfo> newFields = fieldInfos;
final Map<String, FieldIdPB> deletedFieldMap = {
for (var fieldOrder in deletedFields) fieldOrder.fieldId: fieldOrder
};
newFields.retainWhere((field) => (deletedFieldMap[field.id] == null));
_fieldNotifier?.fieldInfos = newFields;
}
void _insertFields(List<IndexFieldPB> insertedFields) {
if (insertedFields.isEmpty) {
return;
}
final List<FieldInfo> newFields = fieldInfos;
for (final indexField in insertedFields) {
final gridField = FieldInfo(field: indexField.field_1);
if (newFields.length > indexField.index) {
newFields.insert(indexField.index, gridField);
} else {
newFields.add(gridField);
}
}
_fieldNotifier?.fieldInfos = newFields;
}
List<FieldInfo> _updateFields(List<FieldPB> updatedFieldPBs) {
if (updatedFieldPBs.isEmpty) {
return [];
}
final List<FieldInfo> newFields = fieldInfos;
final List<FieldInfo> updatedFields = [];
for (final updatedFieldPB in updatedFieldPBs) {
final index =
newFields.indexWhere((field) => field.id == updatedFieldPB.id);
if (index != -1) {
newFields.removeAt(index);
final fieldInfo = FieldInfo(field: updatedFieldPB);
newFields.insert(index, fieldInfo);
updatedFields.add(fieldInfo);
}
}
if (updatedFields.isNotEmpty) {
_fieldNotifier?.fieldInfos = newFields;
}
return updatedFields;
}
}
class GridRowFieldNotifierImpl extends IGridRowFieldNotifier {
final GridFieldController _cache;
OnReceiveUpdateFields? _onChangesetFn;
OnReceiveFields? _onFieldFn;
GridRowFieldNotifierImpl(GridFieldController cache) : _cache = cache;
@override
UnmodifiableListView<FieldInfo> get fields =>
UnmodifiableListView(_cache.fieldInfos);
@override
void onRowFieldsChanged(VoidCallback callback) {
_onFieldFn = (_) => callback();
_cache.addListener(onFields: _onFieldFn);
}
@override
void onRowFieldChanged(void Function(FieldInfo) callback) {
_onChangesetFn = (List<FieldInfo> fieldInfos) {
for (final updatedField in fieldInfos) {
callback(updatedField);
}
};
_cache.addListener(onFieldsUpdated: _onChangesetFn);
}
@override
void onRowDispose() {
if (_onFieldFn != null) {
_cache.removeListener(onFieldsListener: _onFieldFn!);
_onFieldFn = null;
}
if (_onChangesetFn != null) {
_cache.removeListener(onChangesetListener: _onChangesetFn!);
_onChangesetFn = null;
}
}
}
FieldInfo? _findFieldInfoForFilter(
List<FieldInfo> fieldInfos, FilterPB filter) {
final fieldIndex = fieldInfos.indexWhere((element) {
return element.id == filter.fieldId &&
element.fieldType == filter.fieldType;
});
if (fieldIndex != -1) {
return fieldInfos[fieldIndex];
} else {
return null;
}
}
class FieldInfo {
final FieldPB _field;
bool _isGroupField = false;
bool _hasFilter = false;
String get id => _field.id;
FieldType get fieldType => _field.fieldType;
bool get visibility => _field.visibility;
double get width => _field.width.toDouble();
bool get isPrimary => _field.isPrimary;
String get name => _field.name;
FieldPB get field => _field;
bool get isGroupField => _isGroupField;
bool get hasFilter => _hasFilter;
bool get canGroup {
switch (_field.fieldType) {
case FieldType.Checkbox:
return true;
case FieldType.DateTime:
return false;
case FieldType.MultiSelect:
return true;
case FieldType.Number:
return false;
case FieldType.RichText:
return false;
case FieldType.SingleSelect:
return true;
case FieldType.URL:
return false;
}
return false;
}
bool get canCreateFilter {
if (hasFilter) return false;
switch (_field.fieldType) {
case FieldType.Checkbox:
// case FieldType.MultiSelect:
case FieldType.RichText:
case FieldType.SingleSelect:
return true;
default:
return false;
}
}
FieldInfo({required FieldPB field}) : _field = field;
}
@@ -1,51 +0,0 @@
import 'package:app_flowy/core/grid_notification.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
import 'package:flowy_infra/notifier.dart';
import 'dart:async';
import 'dart:typed_data';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
typedef UpdateFieldNotifiedValue = Either<FieldPB, FlowyError>;
class SingleFieldListener {
final String fieldId;
PublishNotifier<UpdateFieldNotifiedValue>? _updateFieldNotifier =
PublishNotifier();
GridNotificationListener? _listener;
SingleFieldListener({required this.fieldId});
void start(
{required void Function(UpdateFieldNotifiedValue) onFieldChanged}) {
_updateFieldNotifier?.addPublishListener(onFieldChanged);
_listener = GridNotificationListener(
objectId: fieldId,
handler: _handler,
);
}
void _handler(
GridDartNotification ty,
Either<Uint8List, FlowyError> result,
) {
switch (ty) {
case GridDartNotification.DidUpdateField:
result.fold(
(payload) =>
_updateFieldNotifier?.value = left(FieldPB.fromBuffer(payload)),
(error) => _updateFieldNotifier?.value = right(error),
);
break;
default:
break;
}
}
Future<void> stop() async {
await _listener?.stop();
_updateFieldNotifier?.dispose();
_updateFieldNotifier = null;
}
}
@@ -1,47 +0,0 @@
import 'package:app_flowy/core/grid_notification.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
import 'package:flowy_infra/notifier.dart';
import 'dart:async';
import 'dart:typed_data';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
typedef UpdateFieldNotifiedValue = Either<GridFieldChangesetPB, FlowyError>;
class GridFieldsListener {
final String gridId;
PublishNotifier<UpdateFieldNotifiedValue>? updateFieldsNotifier =
PublishNotifier();
GridNotificationListener? _listener;
GridFieldsListener({required this.gridId});
void start(
{required void Function(UpdateFieldNotifiedValue) onFieldsChanged}) {
updateFieldsNotifier?.addPublishListener(onFieldsChanged);
_listener = GridNotificationListener(
objectId: gridId,
handler: _handler,
);
}
void _handler(GridDartNotification ty, Either<Uint8List, FlowyError> result) {
switch (ty) {
case GridDartNotification.DidUpdateGridField:
result.fold(
(payload) => updateFieldsNotifier?.value =
left(GridFieldChangesetPB.fromBuffer(payload)),
(error) => updateFieldsNotifier?.value = right(error),
);
break;
default:
break;
}
}
Future<void> stop() async {
await _listener?.stop();
updateFieldsNotifier?.dispose();
updateFieldsNotifier = null;
}
}
@@ -1,26 +0,0 @@
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
class TypeOptionFFIService {
final String gridId;
final String fieldId;
TypeOptionFFIService({
required this.gridId,
required this.fieldId,
});
Future<Either<SelectOptionPB, FlowyError>> newOption({
required String name,
}) {
final payload = CreateSelectOptionPayloadPB.create()
..optionName = name
..gridId = gridId
..fieldId = fieldId;
return GridEventNewSelectOption(payload).send();
}
}
@@ -1,120 +0,0 @@
import 'dart:collection';
import 'package:app_flowy/plugins/grid/presentation/widgets/filter/filter_info.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
import 'dart:async';
import 'package:dartz/dartz.dart';
import 'block/block_cache.dart';
import 'field/field_controller.dart';
import 'prelude.dart';
import 'row/row_cache.dart';
typedef OnFieldsChanged = void Function(List<FieldInfo>);
typedef OnFiltersChanged = void Function(List<FilterInfo>);
typedef OnGridChanged = void Function(GridPB);
typedef OnRowsChanged = void Function(
List<RowInfo> rowInfos,
RowsChangedReason,
);
typedef ListenOnRowChangedCondition = bool Function();
class GridController {
final String gridId;
final GridFFIService _gridFFIService;
final GridFieldController fieldController;
OnRowsChanged? _onRowChanged;
OnGridChanged? _onGridChanged;
// Getters
// key: the block id
final LinkedHashMap<String, GridBlockCache> _blocks;
UnmodifiableMapView<String, GridBlockCache> get blocks =>
UnmodifiableMapView(_blocks);
List<RowInfo> get rowInfos {
final List<RowInfo> rows = [];
for (var block in _blocks.values) {
rows.addAll(block.rows);
}
return rows;
}
GridController({required ViewPB view})
: gridId = view.id,
// ignore: prefer_collection_literals
_blocks = LinkedHashMap(),
_gridFFIService = GridFFIService(gridId: view.id),
fieldController = GridFieldController(gridId: view.id);
void addListener({
OnGridChanged? onGridChanged,
OnRowsChanged? onRowsChanged,
OnFieldsChanged? onFieldsChanged,
OnFiltersChanged? onFiltersChanged,
}) {
_onGridChanged = onGridChanged;
_onRowChanged = onRowsChanged;
fieldController.addListener(
onFields: onFieldsChanged,
onFilters: onFiltersChanged,
);
}
// Loads the rows from each block
Future<Either<Unit, FlowyError>> openGrid() async {
return _gridFFIService.openGrid().then((result) {
return result.fold(
(grid) async {
_initialBlocks(grid.blocks);
_onGridChanged?.call(grid);
final result = await fieldController.loadFields(
fieldIds: grid.fields,
);
return result;
},
(err) => right(err),
);
});
}
Future<void> createRow() async {
await _gridFFIService.createRow();
}
Future<void> dispose() async {
await _gridFFIService.closeGrid();
await fieldController.dispose();
for (final blockCache in _blocks.values) {
blockCache.dispose();
}
}
void _initialBlocks(List<BlockPB> blocks) {
for (final block in blocks) {
if (_blocks[block.id] != null) {
Log.warn("Initial duplicate block's cache: ${block.id}");
return;
}
final cache = GridBlockCache(
gridId: gridId,
block: block,
fieldController: fieldController,
);
cache.addListener(onRowsChanged: (reason) {
_onRowChanged?.call(rowInfos, reason);
});
_blocks[block.id] = cache;
}
}
}
@@ -1,66 +0,0 @@
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
class GridFFIService {
final String gridId;
GridFFIService({
required this.gridId,
});
Future<Either<GridPB, FlowyError>> openGrid() async {
await FolderEventSetLatestView(ViewIdPB(value: gridId)).send();
final payload = GridIdPB(value: gridId);
return GridEventGetGrid(payload).send();
}
Future<Either<RowPB, FlowyError>> createRow({Option<String>? startRowId}) {
var payload = CreateTableRowPayloadPB.create()..gridId = gridId;
startRowId?.fold(() => null, (id) => payload.startRowId = id);
return GridEventCreateTableRow(payload).send();
}
Future<Either<RowPB, FlowyError>> createBoardCard(
String groupId,
String? startRowId,
) {
CreateBoardCardPayloadPB payload = CreateBoardCardPayloadPB.create()
..gridId = gridId
..groupId = groupId;
if (startRowId != null) {
payload.startRowId = startRowId;
}
return GridEventCreateBoardCard(payload).send();
}
Future<Either<List<FieldPB>, FlowyError>> getFields(
{List<FieldIdPB>? fieldIds}) {
var payload = GetFieldPayloadPB.create()..gridId = gridId;
if (fieldIds != null) {
payload.fieldIds = RepeatedFieldIdPB(items: fieldIds);
}
return GridEventGetFields(payload).send().then((result) {
return result.fold((l) => left(l.items), (r) => right(r));
});
}
Future<Either<Unit, FlowyError>> closeGrid() {
final request = ViewIdPB(value: gridId);
return FolderEventCloseView(request).send();
}
Future<Either<RepeatedGroupPB, FlowyError>> loadGroups() {
final payload = GridIdPB(value: gridId);
return GridEventGetGroup(payload).send();
}
}
@@ -1,28 +0,0 @@
export 'grid_bloc.dart';
export 'row/row_bloc.dart';
export 'row/row_service.dart';
export 'grid_service.dart';
export 'grid_header_bloc.dart';
// FieldPB
export 'field/field_service.dart';
export 'field/field_action_sheet_bloc.dart';
export 'field/field_editor_bloc.dart';
export 'field/field_type_option_edit_bloc.dart';
// FieldPB Type Option
export 'field/type_option/date_bloc.dart';
export 'field/type_option/number_bloc.dart';
export 'field/type_option/single_select_type_option.dart';
// CellPB
export 'cell/text_cell_bloc.dart';
export 'cell/number_cell_bloc.dart';
export 'cell/select_option_cell_bloc.dart';
export 'cell/date_cell_bloc.dart';
export 'cell/checkbox_cell_bloc.dart';
export 'cell/cell_service/cell_service.dart';
// Setting
export 'setting/setting_bloc.dart';
export 'setting/property_bloc.dart';
@@ -1,49 +0,0 @@
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_field_notifier.dart';
import 'package:flutter/material.dart';
import '../../presentation/widgets/cell/cell_builder.dart';
import '../cell/cell_service/cell_service.dart';
import '../field/field_controller.dart';
import 'row_cache.dart';
typedef OnRowChanged = void Function(GridCellMap, RowsChangedReason);
class GridRowDataController extends GridCellBuilderDelegate {
final RowInfo rowInfo;
final List<VoidCallback> _onRowChangedListeners = [];
final GridFieldController _fieldController;
final GridRowCache _rowCache;
GridRowDataController({
required this.rowInfo,
required GridFieldController fieldController,
required GridRowCache rowCache,
}) : _fieldController = fieldController,
_rowCache = rowCache;
GridCellMap loadData() {
return _rowCache.loadGridCells(rowInfo.rowPB.id);
}
void addListener({OnRowChanged? onRowChanged}) {
_onRowChangedListeners.add(_rowCache.addListener(
rowId: rowInfo.rowPB.id,
onCellUpdated: onRowChanged,
));
}
void dispose() {
for (final fn in _onRowChangedListeners) {
_rowCache.removeRowListener(fn);
}
}
// GridCellBuilderDelegate implementation
@override
GridCellFieldNotifier buildFieldNotifier() {
return GridCellFieldNotifier(
notifier: GridCellFieldNotifierImpl(_fieldController));
}
@override
GridCellCache get cellCache => _rowCache.cellCache;
}
@@ -1,45 +0,0 @@
import 'package:app_flowy/core/grid_notification.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
import 'package:flowy_infra/notifier.dart';
import 'dart:async';
import 'dart:typed_data';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
typedef UpdateRowNotifiedValue = Either<RowPB, FlowyError>;
typedef UpdateFieldNotifiedValue = Either<List<FieldPB>, FlowyError>;
class RowListener {
final String rowId;
PublishNotifier<UpdateRowNotifiedValue>? updateRowNotifier =
PublishNotifier();
GridNotificationListener? _listener;
RowListener({required this.rowId});
void start() {
_listener = GridNotificationListener(objectId: rowId, handler: _handler);
}
void _handler(GridDartNotification ty, Either<Uint8List, FlowyError> result) {
switch (ty) {
case GridDartNotification.DidUpdateRow:
result.fold(
(payload) =>
updateRowNotifier?.value = left(RowPB.fromBuffer(payload)),
(error) => updateRowNotifier?.value = right(error),
);
break;
default:
break;
}
}
Future<void> stop() async {
await _listener?.stop();
updateRowNotifier?.dispose();
updateRowNotifier = null;
}
}
@@ -1,101 +0,0 @@
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/group_changeset.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
class RowFFIService {
final String gridId;
final String blockId;
RowFFIService({
required this.gridId,
required this.blockId,
});
Future<Either<RowPB, FlowyError>> createRow(String rowId) {
final payload = CreateTableRowPayloadPB.create()
..gridId = gridId
..startRowId = rowId;
return GridEventCreateTableRow(payload).send();
}
Future<Either<OptionalRowPB, FlowyError>> getRow(String rowId) {
final payload = RowIdPB.create()
..gridId = gridId
..blockId = blockId
..rowId = rowId;
return GridEventGetRow(payload).send();
}
Future<Either<Unit, FlowyError>> deleteRow(String rowId) {
final payload = RowIdPB.create()
..gridId = gridId
..blockId = blockId
..rowId = rowId;
return GridEventDeleteRow(payload).send();
}
Future<Either<Unit, FlowyError>> duplicateRow(String rowId) {
final payload = RowIdPB.create()
..gridId = gridId
..blockId = blockId
..rowId = rowId;
return GridEventDuplicateRow(payload).send();
}
}
class MoveRowFFIService {
final String gridId;
MoveRowFFIService({
required this.gridId,
});
Future<Either<Unit, FlowyError>> moveRow({
required String fromRowId,
required String toRowId,
}) {
var payload = MoveRowPayloadPB.create()
..viewId = gridId
..fromRowId = fromRowId
..toRowId = toRowId;
return GridEventMoveRow(payload).send();
}
Future<Either<Unit, FlowyError>> moveGroupRow({
required String fromRowId,
required String toGroupId,
required String? toRowId,
}) {
var payload = MoveGroupRowPayloadPB.create()
..viewId = gridId
..fromRowId = fromRowId
..toGroupId = toGroupId;
if (toRowId != null) {
payload.toRowId = toRowId;
}
return GridEventMoveGroupRow(payload).send();
}
Future<Either<Unit, FlowyError>> moveGroup({
required String fromGroupId,
required String toGroupId,
}) {
final payload = MoveGroupPayloadPB.create()
..viewId = gridId
..fromGroupId = fromGroupId
..toGroupId = toGroupId;
return GridEventMoveGroup(payload).send();
}
}
@@ -1,87 +0,0 @@
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import '../field/field_controller.dart';
import 'setting_service.dart';
part 'group_bloc.freezed.dart';
class GridGroupBloc extends Bloc<GridGroupEvent, GridGroupState> {
final GridFieldController _fieldController;
final SettingFFIService _settingFFIService;
Function(List<FieldInfo>)? _onFieldsFn;
GridGroupBloc({
required String viewId,
required GridFieldController fieldController,
}) : _fieldController = fieldController,
_settingFFIService = SettingFFIService(viewId: viewId),
super(GridGroupState.initial(viewId, fieldController.fieldInfos)) {
on<GridGroupEvent>(
(event, emit) async {
event.when(
initial: () {
_startListening();
},
didReceiveFieldUpdate: (fieldContexts) {
emit(state.copyWith(fieldContexts: fieldContexts));
},
setGroupByField: (String fieldId, FieldType fieldType) async {
final result = await _settingFFIService.groupByField(
fieldId: fieldId,
fieldType: fieldType,
);
result.fold((l) => null, (err) => Log.error(err));
},
);
},
);
}
@override
Future<void> close() async {
if (_onFieldsFn != null) {
_fieldController.removeListener(onFieldsListener: _onFieldsFn!);
_onFieldsFn = null;
}
return super.close();
}
void _startListening() {
_onFieldsFn = (fieldContexts) =>
add(GridGroupEvent.didReceiveFieldUpdate(fieldContexts));
_fieldController.addListener(
onFields: _onFieldsFn,
listenWhen: () => !isClosed,
);
}
}
@freezed
class GridGroupEvent with _$GridGroupEvent {
const factory GridGroupEvent.initial() = _Initial;
const factory GridGroupEvent.setGroupByField(
String fieldId,
FieldType fieldType,
) = _GroupByField;
const factory GridGroupEvent.didReceiveFieldUpdate(List<FieldInfo> fields) =
_DidReceiveFieldUpdate;
}
@freezed
class GridGroupState with _$GridGroupState {
const factory GridGroupState({
required String gridId,
required List<FieldInfo> fieldContexts,
}) = _GridGroupState;
factory GridGroupState.initial(
String gridId, List<FieldInfo> fieldContexts) =>
GridGroupState(
gridId: gridId,
fieldContexts: fieldContexts,
);
}
@@ -1,47 +0,0 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import 'package:dartz/dartz.dart';
part 'setting_bloc.freezed.dart';
class GridSettingBloc extends Bloc<GridSettingEvent, GridSettingState> {
final String gridId;
GridSettingBloc({required this.gridId}) : super(GridSettingState.initial()) {
on<GridSettingEvent>(
(event, emit) async {
event.map(performAction: (_PerformAction value) {
emit(state.copyWith(selectedAction: Some(value.action)));
});
},
);
}
@override
Future<void> close() async {
return super.close();
}
}
@freezed
class GridSettingEvent with _$GridSettingEvent {
const factory GridSettingEvent.performAction(GridSettingAction action) =
_PerformAction;
}
@freezed
class GridSettingState with _$GridSettingState {
const factory GridSettingState({
required Option<GridSettingAction> selectedAction,
}) = _GridSettingState;
factory GridSettingState.initial() => GridSettingState(
selectedAction: none(),
);
}
enum GridSettingAction {
showFilters,
sortBy,
showProperties,
}
@@ -1,47 +0,0 @@
import 'dart:typed_data';
import 'package:app_flowy/core/grid_notification.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_infra/notifier.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pbserver.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/setting_entities.pb.dart';
typedef UpdateSettingNotifiedValue = Either<GridSettingPB, FlowyError>;
class SettingListener {
final String gridId;
GridNotificationListener? _listener;
PublishNotifier<UpdateSettingNotifiedValue>? _updateSettingNotifier =
PublishNotifier();
SettingListener({required this.gridId});
void start({
required void Function(UpdateSettingNotifiedValue) onSettingUpdated,
}) {
_updateSettingNotifier?.addPublishListener(onSettingUpdated);
_listener = GridNotificationListener(objectId: gridId, handler: _handler);
}
void _handler(GridDartNotification ty, Either<Uint8List, FlowyError> result) {
switch (ty) {
case GridDartNotification.DidUpdateGridSetting:
result.fold(
(payload) => _updateSettingNotifier?.value = left(
GridSettingPB.fromBuffer(payload),
),
(error) => _updateSettingNotifier?.value = right(error),
);
break;
default:
break;
}
}
Future<void> stop() async {
await _listener?.stop();
_updateSettingNotifier?.dispose();
_updateSettingNotifier = null;
}
}
@@ -1,32 +0,0 @@
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/setting_entities.pb.dart';
class SettingFFIService {
final String viewId;
const SettingFFIService({required this.viewId});
Future<Either<GridSettingPB, FlowyError>> getSetting() {
final payload = GridIdPB.create()..value = viewId;
return GridEventGetGridSetting(payload).send();
}
Future<Either<Unit, FlowyError>> groupByField({
required String fieldId,
required FieldType fieldType,
}) {
final insertGroupPayload = InsertGroupPayloadPB.create()
..fieldId = fieldId
..fieldType = fieldType;
final payload = GridSettingChangesetPB.create()
..gridId = viewId
..insertGroup = insertGroupPayload;
return GridEventUpdateGridSetting(payload).send();
}
}
@@ -1,73 +0,0 @@
/// The data structor representing each selection of flowy table.
enum FlowyTableSelectionType {
item,
row,
col,
}
class FlowyTableSelectionItem {
final FlowyTableSelectionType type;
final int? row;
final int? column;
const FlowyTableSelectionItem({
required this.type,
this.row,
this.column,
});
@override
String toString() {
return '$type($row, $column)';
}
@override
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}
return other is FlowyTableSelectionItem &&
type == other.type &&
row == other.row &&
column == other.column;
}
@override
int get hashCode => type.hashCode ^ row.hashCode ^ column.hashCode;
}
class FlowyTableSelection {
Set<FlowyTableSelectionItem> _items = {};
Set<FlowyTableSelectionItem> get items => _items;
FlowyTableSelection(
this._items,
);
FlowyTableSelection.combine(
FlowyTableSelection lhs, FlowyTableSelection rhs) {
this..combine(lhs)..combine(rhs);
}
FlowyTableSelection operator +(FlowyTableSelection other) {
return this..combine(other);
}
void combine(FlowyTableSelection other) {
var totalItems = items..union(other.items);
final rows = totalItems
.where((ele) => ele.type == FlowyTableSelectionType.row)
.map((e) => e.row)
.toSet();
final cols = totalItems
.where((ele) => ele.type == FlowyTableSelectionType.col)
.map((e) => e.column)
.toSet();
totalItems.removeWhere((ele) {
return ele.type == FlowyTableSelectionType.item &&
(rows.contains(ele.row) || cols.contains(ele.column));
});
_items = totalItems;
}
}
@@ -1,7 +0,0 @@
export 'cell_builder.dart';
export 'text_cell.dart';
export 'number_cell.dart';
export 'date_cell/date_cell.dart';
export 'checkbox_cell.dart';
export 'select_option_cell/select_option_cell.dart';
export 'url_cell/url_cell.dart';
@@ -1,112 +0,0 @@
import 'package:flowy_infra/text_style.dart';
import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:textstyle_extensions/textstyle_extensions.dart';
class InputTextField extends StatefulWidget {
final void Function(String)? onDone;
final void Function(String)? onChanged;
final void Function() onCanceled;
final bool autoClearWhenDone;
final String text;
final int? maxLength;
final FocusNode? focusNode;
const InputTextField({
required this.text,
this.onDone,
required this.onCanceled,
this.onChanged,
this.autoClearWhenDone = false,
this.maxLength,
this.focusNode,
Key? key,
}) : super(key: key);
@override
State<InputTextField> createState() => _InputTextFieldState();
}
class _InputTextFieldState extends State<InputTextField> {
late FocusNode _focusNode;
var isEdited = false;
late TextEditingController _controller;
@override
void initState() {
_focusNode = widget.focusNode ?? FocusNode();
_controller = TextEditingController(text: widget.text);
SchedulerBinding.instance.addPostFrameCallback((Duration _) {
_focusNode.requestFocus();
});
_focusNode.addListener(notifyDidEndEditing);
super.initState();
}
@override
Widget build(BuildContext context) {
final height = widget.maxLength == null ? 36.0 : 56.0;
return RoundedInputField(
controller: _controller,
focusNode: _focusNode,
autoFocus: true,
height: height,
maxLength: widget.maxLength,
style: TextStyles.body1.size(13),
onChanged: (text) {
if (widget.onChanged != null) {
widget.onChanged!(text);
}
},
onEditingComplete: (_) {
if (widget.onDone != null) {
widget.onDone!(_controller.text);
}
if (widget.autoClearWhenDone) {
_controller.text = "";
}
},
);
}
@override
void dispose() {
_focusNode.removeListener(notifyDidEndEditing);
// only dispose the focusNode if it was created in this widget's initState
if (widget.focusNode == null) {
_focusNode.dispose();
}
super.dispose();
}
void notifyDidEndEditing() {
if (!_focusNode.hasFocus) {
if (_controller.text.isEmpty) {
widget.onCanceled();
} else {
if (widget.onDone != null) {
widget.onDone!(_controller.text);
}
}
}
}
}
class TypeOptionSeparator extends StatelessWidget {
const TypeOptionSeparator({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Container(
color: Theme.of(context).dividerColor,
height: 1.0,
),
);
}
}
@@ -1,53 +0,0 @@
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_filter.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_filter.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_option_filter.pbserver.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/text_filter.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/util.pb.dart';
class FilterInfo {
final String viewId;
final FilterPB filter;
final FieldInfo field;
FilterInfo(this.viewId, this.filter, this.field);
FilterInfo copyWith({FilterPB? filter, FieldInfo? field}) {
return FilterInfo(
viewId,
filter ?? this.filter,
field ?? this.field,
);
}
DateFilterPB? dateFilter() {
if (filter.fieldType != FieldType.DateTime) {
return null;
}
return DateFilterPB.fromBuffer(filter.data);
}
TextFilterPB? textFilter() {
if (filter.fieldType != FieldType.RichText) {
return null;
}
return TextFilterPB.fromBuffer(filter.data);
}
CheckboxFilterPB? checkboxFilter() {
if (filter.fieldType != FieldType.Checkbox) {
return null;
}
return CheckboxFilterPB.fromBuffer(filter.data);
}
SelectOptionFilterPB? selectOptionFilter() {
if (filter.fieldType == FieldType.SingleSelect ||
filter.fieldType == FieldType.MultiSelect) {
return SelectOptionFilterPB.fromBuffer(filter.data);
} else {
return null;
}
}
}
@@ -1,76 +0,0 @@
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra/text_style.dart';
import 'package:flutter/material.dart';
import 'package:textstyle_extensions/textstyle_extensions.dart';
class FilterTextField extends StatefulWidget {
final String hintText;
final String text;
final void Function(String)? onChanged;
final void Function(String)? onSubmitted;
final bool autoFucous;
const FilterTextField({
this.hintText = "",
this.text = "",
this.onChanged,
this.onSubmitted,
this.autoFucous = true,
Key? key,
}) : super(key: key);
@override
State<FilterTextField> createState() => FilterTextFieldState();
}
class FilterTextFieldState extends State<FilterTextField> {
late FocusNode focusNode;
late TextEditingController controller;
@override
void initState() {
focusNode = FocusNode();
controller = TextEditingController();
controller.text = widget.text;
if (widget.autoFucous) {
WidgetsBinding.instance.addPostFrameCallback((_) {
focusNode.requestFocus();
});
}
super.initState();
}
@override
Widget build(BuildContext context) {
return TextField(
controller: controller,
focusNode: focusNode,
onChanged: (text) {
widget.onChanged?.call(text);
},
onSubmitted: (text) {
widget.onSubmitted?.call(text);
},
maxLines: 1,
style: TextStyles.body1.size(FontSizes.s12),
decoration: InputDecoration(
contentPadding: const EdgeInsets.all(10),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).colorScheme.primary,
width: 1.0,
),
borderRadius: Corners.s10Border,
),
isDense: true,
hintText: widget.hintText,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).colorScheme.primary,
width: 1.0,
),
borderRadius: Corners.s8Border,
),
),
);
}
}
@@ -1,140 +0,0 @@
import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
import 'package:app_flowy/plugins/grid/presentation/widgets/header/field_editor.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/plugins/grid/application/setting/property_bloc.dart';
import 'package:app_flowy/plugins/grid/presentation/widgets/header/field_type_extension.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:styled_widget/styled_widget.dart';
import '../../../application/field/field_controller.dart';
import '../../layout/sizes.dart';
class GridPropertyList extends StatefulWidget {
final String gridId;
final GridFieldController fieldController;
const GridPropertyList({
required this.gridId,
required this.fieldController,
Key? key,
}) : super(key: key);
@override
State<StatefulWidget> createState() => _GridPropertyListState();
}
class _GridPropertyListState extends State<GridPropertyList> {
late PopoverMutex _popoverMutex;
@override
void initState() {
_popoverMutex = PopoverMutex();
super.initState();
}
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => getIt<GridPropertyBloc>(
param1: widget.gridId, param2: widget.fieldController)
..add(const GridPropertyEvent.initial()),
child: BlocBuilder<GridPropertyBloc, GridPropertyState>(
builder: (context, state) {
final cells = state.fieldContexts.map((field) {
return _GridPropertyCell(
popoverMutex: _popoverMutex,
gridId: widget.gridId,
fieldInfo: field,
key: ValueKey(field.id),
);
}).toList();
return ListView.separated(
controller: ScrollController(),
shrinkWrap: true,
itemCount: cells.length,
itemBuilder: (BuildContext context, int index) {
return cells[index];
},
separatorBuilder: (BuildContext context, int index) {
return VSpace(GridSize.typeOptionSeparatorHeight);
},
);
},
),
);
}
}
class _GridPropertyCell extends StatelessWidget {
final FieldInfo fieldInfo;
final String gridId;
final PopoverMutex popoverMutex;
const _GridPropertyCell({
required this.gridId,
required this.fieldInfo,
required this.popoverMutex,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final checkmark = svgWidget(
fieldInfo.visibility ? 'home/show' : 'home/hide',
color: Theme.of(context).colorScheme.onSurface,
);
return Row(
children: [
Expanded(
child: SizedBox(
height: GridSize.typeOptionItemHeight,
child: _editFieldButton(context),
),
),
FlowyIconButton(
width: GridSize.typeOptionItemHeight,
onPressed: () {
context.read<GridPropertyBloc>().add(
GridPropertyEvent.setFieldVisibility(
fieldInfo.id, !fieldInfo.visibility));
},
icon: checkmark.padding(all: 6),
)
],
);
}
Widget _editFieldButton(BuildContext context) {
return AppFlowyPopover(
mutex: popoverMutex,
offset: const Offset(20, 0),
direction: PopoverDirection.leftWithTopAligned,
constraints: BoxConstraints.loose(const Size(240, 400)),
child: FlowyButton(
text: FlowyText.medium(fieldInfo.name),
leftIcon: svgWidget(
fieldInfo.fieldType.iconName(),
color: Theme.of(context).colorScheme.onSurface,
),
),
popupBuilder: (BuildContext context) {
return FieldEditor(
gridId: gridId,
fieldName: fieldInfo.name,
typeOptionLoader: FieldTypeOptionLoader(
gridId: gridId,
field: fieldInfo.field,
),
);
},
);
}
}
@@ -1,46 +0,0 @@
import 'package:app_flowy/startup/plugin/plugin.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart';
import 'package:app_flowy/generated/locale_keys.g.dart';
class MenuTrash extends StatelessWidget {
const MenuTrash({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
height: 26,
child: InkWell(
onTap: () {
getIt<MenuSharedState>().latestOpenView = null;
getIt<HomeStackManager>()
.setPlugin(makePlugin(pluginType: PluginType.trash));
},
child: _render(context),
),
);
}
Widget _render(BuildContext context) {
return Row(
children: [
SizedBox(
width: 16,
height: 16,
child: svgWidget(
"home/trash",
color: Theme.of(context).colorScheme.onSurface,
),
),
const HSpace(6),
FlowyText.medium(LocaleKeys.trash_text.tr()),
],
);
}
}
@@ -1,29 +0,0 @@
import 'dart:io';
import 'package:app_flowy/startup/startup.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flowy_sdk/flowy_sdk.dart';
class InitRustSDKTask extends LaunchTask {
@override
LaunchTaskType get type => LaunchTaskType.dataProcessing;
@override
Future<void> initialize(LaunchContext context) async {
await appFlowyDocumentDirectory().then((directory) async {
await context.getIt<FlowySDK>().init(directory);
});
}
}
Future<Directory> appFlowyDocumentDirectory() async {
switch (integrationEnv()) {
case IntegrationMode.develop:
Directory documentsDir = await getApplicationDocumentsDirectory();
return Directory('${documentsDir.path}/flowy_dev').create();
case IntegrationMode.release:
Directory documentsDir = await getApplicationDocumentsDirectory();
return Directory('${documentsDir.path}/flowy').create();
case IntegrationMode.test:
return Directory("${Directory.current.path}/.sandbox");
}
}
@@ -1,150 +0,0 @@
import 'package:app_flowy/user/application/auth_service.dart';
import 'package:app_flowy/user/presentation/router.dart';
import 'package:app_flowy/user/presentation/widgets/background.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra/uuid.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/rounded_button.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/protobuf.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:dartz/dartz.dart' as dartz;
import 'package:app_flowy/generated/locale_keys.g.dart';
class SkipLogInScreen extends StatefulWidget {
final AuthRouter router;
final AuthService authService;
const SkipLogInScreen({
Key? key,
required this.router,
required this.authService,
}) : super(key: key);
@override
State<SkipLogInScreen> createState() => _SkipLogInScreenState();
}
class _SkipLogInScreenState extends State<SkipLogInScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
width: 400,
height: 600,
child: _renderBody(context),
),
),
);
}
Widget _renderBody(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FlowyLogoTitle(
title: LocaleKeys.welcomeText.tr(),
logoSize: const Size.square(60),
),
const VSpace(80),
GoButton(onPressed: () => _autoRegister(context)),
const VSpace(30),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
InkWell(
hoverColor: Colors.transparent,
onTap: () =>
_launchURL('https://github.com/AppFlowy-IO/appflowy'),
child: FlowyText.medium(
LocaleKeys.githubStarText.tr(),
color: Theme.of(context).colorScheme.primary,
decoration: TextDecoration.underline,
),
),
InkWell(
hoverColor: Colors.transparent,
onTap: () => _launchURL('https://www.appflowy.io/blog'),
child: FlowyText.medium(
LocaleKeys.subscribeNewsletterText.tr(),
color: Theme.of(context).colorScheme.primary,
decoration: TextDecoration.underline,
),
),
],
)
],
);
}
_launchURL(String url) async {
final uri = Uri.parse(url);
if (await canLaunchUrl(uri)) {
await launchUrl(uri);
} else {
throw 'Could not launch $url';
}
}
void _autoRegister(BuildContext context) async {
const password = "AppFlowy123@";
final uid = uuid();
final userEmail = "$uid@appflowy.io";
final result = await widget.authService.signUp(
name: LocaleKeys.defaultUsername.tr(),
password: password,
email: userEmail,
);
result.fold(
(user) {
FolderEventReadCurrentWorkspace().send().then((result) {
_openCurrentWorkspace(context, user, result);
});
},
(error) {
Log.error(error);
},
);
}
void _openCurrentWorkspace(
BuildContext context,
UserProfilePB user,
dartz.Either<WorkspaceSettingPB, FlowyError> workspacesOrError,
) {
workspacesOrError.fold(
(workspaceSetting) {
widget.router.pushHomeScreen(context, user, workspaceSetting);
},
(error) {
Log.error(error);
},
);
}
}
class GoButton extends StatelessWidget {
final VoidCallback onPressed;
const GoButton({
Key? key,
required this.onPressed,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return RoundedTextButton(
title: LocaleKeys.letsGoButtonText.tr(),
fontSize: FontSizes.s16,
height: 50,
borderRadius: Corners.s10Border,
onPressed: onPressed,
);
}
}
@@ -1,80 +0,0 @@
import 'dart:async';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:app_flowy/startup/plugin/plugin.dart';
class AppService {
Future<Either<AppPB, FlowyError>> readApp({required String appId}) {
final payload = AppIdPB.create()..value = appId;
return FolderEventReadApp(payload).send();
}
Future<Either<ViewPB, FlowyError>> createView({
required String appId,
required String name,
String? desc,
required ViewDataFormatPB dataFormatType,
required PluginType pluginType,
required ViewLayoutTypePB layoutType,
}) {
var payload = CreateViewPayloadPB.create()
..belongToId = appId
..name = name
..desc = desc ?? ""
..dataFormat = dataFormatType
..layout = layoutType;
return FolderEventCreateView(payload).send();
}
Future<Either<List<ViewPB>, FlowyError>> getViews({required String appId}) {
final payload = AppIdPB.create()..value = appId;
return FolderEventReadApp(payload).send().then((result) {
return result.fold(
(app) => left(app.belongings.items),
(error) => right(error),
);
});
}
Future<Either<Unit, FlowyError>> delete({required String appId}) {
final request = AppIdPB.create()..value = appId;
return FolderEventDeleteApp(request).send();
}
Future<Either<Unit, FlowyError>> deleteView({required String viewId}) {
final request = RepeatedViewIdPB.create()..items.add(viewId);
return FolderEventDeleteView(request).send();
}
Future<Either<Unit, FlowyError>> updateApp(
{required String appId, String? name}) {
UpdateAppPayloadPB payload = UpdateAppPayloadPB.create()..appId = appId;
if (name != null) {
payload.name = name;
}
return FolderEventUpdateApp(payload).send();
}
Future<Either<Unit, FlowyError>> moveView({
required String viewId,
required int fromIndex,
required int toIndex,
}) {
final payload = MoveFolderItemPayloadPB.create()
..itemId = viewId
..from = fromIndex
..to = toIndex
..ty = MoveFolderItemType.MoveView;
return FolderEventMoveFolderItem(payload).send();
}
}
@@ -1,139 +0,0 @@
import 'dart:async';
import 'package:app_flowy/user/application/user_settings_service.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-user/user_setting.pb.dart';
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'appearance.freezed.dart';
/// [AppearanceSettingsCubit] is used to modify the appear setting of AppFlowy application. Includes the [Locale] and [AppTheme].
class AppearanceSettingsCubit extends Cubit<AppearanceSettingsState> {
final AppearanceSettingsPB _setting;
AppearanceSettingsCubit(AppearanceSettingsPB setting)
: _setting = setting,
super(AppearanceSettingsState.initial(
setting.theme,
setting.font,
setting.monospaceFont,
setting.locale,
));
/// Updates the current theme and notify the listeners the theme was changed.
/// Do nothing if the passed in themeType equal to the current theme type.
void setTheme(Brightness brightness) {
if (state.theme.brightness == brightness) {
return;
}
_setting.theme = themeTypeToString(brightness);
_saveAppearanceSettings();
emit(state.copyWith(
theme: AppTheme.fromName(
themeName: _setting.theme,
font: state.theme.font,
monospaceFont: state.theme.monospaceFont,
),
));
}
/// Updates the current locale and notify the listeners the locale was changed
/// Fallback to [en] locale If the newLocale is not supported.
void setLocale(BuildContext context, Locale newLocale) {
if (!context.supportedLocales.contains(newLocale)) {
Log.warn("Unsupported locale: $newLocale, Fallback to locale: en");
newLocale = const Locale('en');
}
context.setLocale(newLocale);
if (state.locale != newLocale) {
_setting.locale.languageCode = newLocale.languageCode;
_setting.locale.countryCode = newLocale.countryCode ?? "";
_saveAppearanceSettings();
emit(state.copyWith(locale: newLocale));
}
}
/// Saves key/value setting to disk.
/// Removes the key if the passed in value is null
void setKeyValue(String key, String? value) {
if (key.isEmpty) {
Log.warn("The key should not be empty");
return;
}
if (value == null) {
_setting.settingKeyValue.remove(key);
}
if (_setting.settingKeyValue[key] != value) {
if (value == null) {
_setting.settingKeyValue.remove(key);
} else {
_setting.settingKeyValue[key] = value;
}
}
_saveAppearanceSettings();
}
String? getValue(String key) {
if (key.isEmpty) {
Log.warn("The key should not be empty");
return null;
}
return _setting.settingKeyValue[key];
}
/// Called when the application launch.
/// Uses the device locale when open the application for the first time
void readLocaleWhenAppLaunch(BuildContext context) {
if (_setting.resetToDefault) {
_setting.resetToDefault = false;
_saveAppearanceSettings();
setLocale(context, context.deviceLocale);
return;
}
setLocale(context, state.locale);
}
Future<void> _saveAppearanceSettings() async {
SettingsFFIService().setAppearanceSetting(_setting).then((result) {
result.fold(
(l) => null,
(error) => Log.error(error),
);
});
}
}
@freezed
class AppearanceSettingsState with _$AppearanceSettingsState {
const factory AppearanceSettingsState({
required AppTheme theme,
required Locale locale,
}) = _AppearanceSettingsState;
factory AppearanceSettingsState.initial(
String themeName,
String font,
String monospaceFont,
LocaleSettingsPB locale,
) =>
AppearanceSettingsState(
theme: AppTheme.fromName(
themeName: themeName,
font: font,
monospaceFont: monospaceFont,
),
locale: Locale(locale.languageCode, locale.countryCode),
);
}
@@ -1,62 +0,0 @@
import 'package:app_flowy/startup/plugin/plugin.dart';
import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
import 'package:flutter/material.dart';
import 'package:styled_widget/styled_widget.dart';
class AddButton extends StatelessWidget {
final Function(PluginBuilder) onSelected;
const AddButton({
Key? key,
required this.onSelected,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final List<PopoverAction> actions = [];
actions.addAll(
pluginBuilders()
.map((pluginBuilder) =>
AddButtonActionWrapper(pluginBuilder: pluginBuilder))
.toList(),
);
return PopoverActionList<PopoverAction>(
direction: PopoverDirection.bottomWithLeftAligned,
actions: actions,
buildChild: (controller) {
return FlowyIconButton(
width: 22,
onPressed: () => controller.show(),
icon: svgWidget(
"home/add",
color: Theme.of(context).colorScheme.onSurface,
).padding(horizontal: 3, vertical: 3),
);
},
onSelected: (action, controller) {
if (action is AddButtonActionWrapper) {
onSelected(action.pluginBuilder);
}
controller.close();
},
);
}
}
class AddButtonActionWrapper extends ActionCell {
final PluginBuilder pluginBuilder;
AddButtonActionWrapper({required this.pluginBuilder});
@override
Widget? leftIcon(Color iconColor) =>
svgWidget(pluginBuilder.menuIcon, color: iconColor);
@override
String get name => pluginBuilder.menuName;
}
@@ -1,43 +0,0 @@
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/workspace/application/appearance.dart';
import 'package:app_flowy/workspace/presentation/widgets/toggle/toggle_style.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../widgets/toggle/toggle.dart';
class SettingsAppearanceView extends StatelessWidget {
const SettingsAppearanceView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
FlowyText.medium(LocaleKeys.settings_appearance_lightLabel.tr()),
Toggle(
value: Theme.of(context).brightness == Brightness.dark,
onChanged: (_) => setTheme(context),
style: ToggleStyle.big,
),
FlowyText.medium(LocaleKeys.settings_appearance_darkLabel.tr())
],
),
],
),
);
}
void setTheme(BuildContext context) {
if (Theme.of(context).brightness == Brightness.dark) {
context.read<AppearanceSettingsCubit>().setTheme(Brightness.light);
} else {
context.read<AppearanceSettingsCubit>().setTheme(Brightness.dark);
}
}
}
@@ -1,50 +0,0 @@
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/workspace/presentation/settings/widgets/settings_menu_element.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
class SettingsMenu extends StatelessWidget {
const SettingsMenu({
Key? key,
required this.changeSelectedIndex,
required this.currentIndex,
}) : super(key: key);
final Function changeSelectedIndex;
final int currentIndex;
@override
Widget build(BuildContext context) {
return Column(
children: [
SettingsMenuElement(
index: 0,
currentIndex: currentIndex,
label: LocaleKeys.settings_menu_appearance.tr(),
icon: Icons.brightness_4,
changeSelectedIndex: changeSelectedIndex,
),
const SizedBox(
height: 10,
),
SettingsMenuElement(
index: 1,
currentIndex: currentIndex,
label: LocaleKeys.settings_menu_language.tr(),
icon: Icons.translate,
changeSelectedIndex: changeSelectedIndex,
),
const SizedBox(
height: 10,
),
SettingsMenuElement(
index: 2,
currentIndex: currentIndex,
label: LocaleKeys.settings_menu_user.tr(),
icon: Icons.account_box_outlined,
changeSelectedIndex: changeSelectedIndex,
),
],
);
}
}
@@ -1,143 +0,0 @@
import 'package:flutter/material.dart';
import 'package:app_flowy/workspace/presentation/widgets/emoji_picker/emoji_picker.dart';
class FlowyEmojiStyleButton extends StatefulWidget {
// final Attribute attribute;
final String normalIcon;
// TODO: enable insert emoji in appflowy_editor
// final QuillController controller;
final String tooltipText;
const FlowyEmojiStyleButton({
// required this.attribute,
required this.normalIcon,
required this.tooltipText,
Key? key,
}) : super(key: key);
@override
EmojiStyleButtonState createState() => EmojiStyleButtonState();
}
class EmojiStyleButtonState extends State<FlowyEmojiStyleButton> {
// Style get _selectionStyle => widget.controller.getSelectionStyle();
final GlobalKey emojiButtonKey = GlobalKey();
OverlayEntry? _entry;
// final FocusNode _keyFocusNode = FocusNode();
@override
void initState() {
super.initState();
// _isToggled = _getIsToggled(_selectionStyle.attributes);
// widget.controller.addListener(_didChangeEditingValue);
}
@override
Widget build(BuildContext context) {
// debugPrint(MediaQuery.of(context).size.width.toString());
// debugPrint(MediaQuery.of(context).size.height.toString());
// return ToolbarIconButton(
// key: emojiButtonKey,
// onPressed: _toggleAttribute,
// width: widget.iconSize * kIconButtonFactor,
// isToggled: _isToggled,
// iconName: widget.normalIcon,
// tooltipText: widget.tooltipText,
// );
return Container();
}
@override
void dispose() {
_entry?.remove();
super.dispose();
}
// void _toggleAttribute() {
// if (_entry?.mounted ?? false) {
// _entry?.remove();
// _entry = null;
// setState(() => _isToggled = false);
// } else {
// RenderBox box =
// emojiButtonKey.currentContext?.findRenderObject() as RenderBox;
// Offset position = box.localToGlobal(Offset.zero);
// // final window = await getWindowInfo();
// _entry = OverlayEntry(
// builder: (BuildContext context) => BuildEmojiPickerView(
// controller: widget.controller,
// offset: position,
// ),
// );
// Overlay.of(context)!.insert(_entry!);
// setState(() => _isToggled = true);
// }
// }
}
class BuildEmojiPickerView extends StatefulWidget {
const BuildEmojiPickerView({Key? key, this.offset}) : super(key: key);
final Offset? offset;
@override
State<BuildEmojiPickerView> createState() => _BuildEmojiPickerViewState();
}
class _BuildEmojiPickerViewState extends State<BuildEmojiPickerView> {
@override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned(
top: widget.offset!.dy -
MediaQuery.of(context).size.height / 2.83 -
30,
left:
widget.offset!.dx - MediaQuery.of(context).size.width / 3.92 + 40,
child: Material(
borderRadius: BorderRadius.circular(8.0),
child: SizedBox(
height: MediaQuery.of(context).size.height / 2.83 + 20,
width: MediaQuery.of(context).size.width / 3.92,
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: EmojiPicker(
onEmojiSelected: (category, emoji) => insertEmoji(emoji),
config: const Config(
columns: 8,
emojiSizeMax: 28,
bgColor: Color(0xffF2F2F2),
iconColor: Colors.grey,
iconColorSelected: Color(0xff333333),
indicatorColor: Color(0xff333333),
progressIndicatorColor: Color(0xff333333),
buttonMode: ButtonMode.CUPERTINO,
initCategory: Category.RECENT,
),
),
),
),
),
),
],
);
}
void insertEmoji(Emoji emoji) {
// final baseOffset = widget.controller.selection.baseOffset;
// final extentOffset = widget.controller.selection.extentOffset;
// final replaceLen = extentOffset - baseOffset;
// final selection = widget.controller.selection.copyWith(
// baseOffset: baseOffset + emoji.emoji.length,
// extentOffset: baseOffset + emoji.emoji.length,
// );
// widget.controller
// .replaceText(baseOffset, replaceLen, emoji.emoji, selection);
}
}
@@ -1,31 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
#include <flowy_infra_ui/flowy_infra_u_i_plugin.h>
#include <hotkey_manager/hotkey_manager_plugin.h>
#include <rich_clipboard_linux/rich_clipboard_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
#include <window_size/window_size_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flowy_infra_ui_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlowyInfraUIPlugin");
flowy_infra_u_i_plugin_register_with_registrar(flowy_infra_ui_registrar);
g_autoptr(FlPluginRegistrar) hotkey_manager_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "HotkeyManagerPlugin");
hotkey_manager_plugin_register_with_registrar(hotkey_manager_registrar);
g_autoptr(FlPluginRegistrar) rich_clipboard_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "RichClipboardPlugin");
rich_clipboard_plugin_register_with_registrar(rich_clipboard_linux_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
g_autoptr(FlPluginRegistrar) window_size_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowSizePlugin");
window_size_plugin_register_with_registrar(window_size_registrar);
}
@@ -1,15 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_
@@ -1,28 +0,0 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
flowy_infra_ui
hotkey_manager
rich_clipboard_linux
url_launcher_linux
window_size
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)
@@ -1,32 +0,0 @@
//
// Generated file. Do not edit.
//
import FlutterMacOS
import Foundation
import connectivity_plus_macos
import device_info_plus_macos
import flowy_infra_ui
import flowy_sdk
import hotkey_manager
import package_info_plus_macos
import path_provider_macos
import rich_clipboard_macos
import shared_preferences_macos
import url_launcher_macos
import window_size
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FlowyInfraUIPlugin.register(with: registry.registrar(forPlugin: "FlowyInfraUIPlugin"))
FlowySdkPlugin.register(with: registry.registrar(forPlugin: "FlowySdkPlugin"))
HotkeyManagerPlugin.register(with: registry.registrar(forPlugin: "HotkeyManagerPlugin"))
FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
RichClipboardPlugin.register(with: registry.registrar(forPlugin: "RichClipboardPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WindowSizePlugin.register(with: registry.registrar(forPlugin: "WindowSizePlugin"))
}
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>
@@ -1,36 +0,0 @@
# 0.0.9
* Enable slide to select text in card
* Fix some bugs
# 0.0.8
* Enable drag and drop group
# 0.0.7
* Rename some classes
* Add documentation
# 0.0.6
* Support scroll to bottom
* Fix some bugs
# 0.0.5
* Optimize insert card animation
* Enable insert card at the end of the group
* Fix some bugs
# 0.0.4
* Fix some bugs
# 0.0.3
* Support customize UI
* Update example
* Add AppFlowy style widget
# 0.0.2
* Update documentation
# 0.0.1
* Support drag and drop group items from one to another
@@ -1,661 +0,0 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
@@ -1,140 +0,0 @@
# appflowy_board
<h1 align="center"><b>AppFlowy Board</b></h1>
<p align="center">A customizable and draggable Kanban Board widget for Flutter</p>
<p align="center">
<a href="https://discord.gg/ZCCYN4Anzq"><b>Discord</b></a> •
<a href="https://twitter.com/appflowy"><b>Twitter</b></a>
</p>
<p align="center">
<img src="https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/app_flowy/packages/appflowy_board/example/gifs/appflowy_board_video_1.gif?raw=true" width="680" title="AppFlowyBoard">
</p>
## Intro
appflowy_board is a customizable and draggable Kanban Board widget for Flutter.
You can use it to create a Kanban Board tool like those in Trello.
Check out [AppFlowy](https://github.com/AppFlowy-IO/AppFlowy) to see how appflowy_board is used to build a BoardView database.
<p align="center">
<img src="https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/app_flowy/packages/appflowy_board/example/gifs/appflowy_board_video_2.gif?raw=true" width="680" title="AppFlowyBoard">
</p>
## Getting Started
Add the AppFlowy Board [Flutter package](https://docs.flutter.dev/development/packages-and-plugins/using-packages) to your environment.
With Flutter:
```dart
flutter pub add appflowy_board
flutter pub get
```
This will add a line like this to your package's pubspec.yaml:
```dart
dependencies:
appflowy_board: ^0.0.6
```
## Create your first board
Initialize an `AppFlowyBoardController` for the board. It contains the data used by the board. You can
register callbacks to receive the changes of the board.
```dart
final AppFlowyBoardController controller = AppFlowyBoardController(
onMoveGroup: (fromGroupId, fromIndex, toGroupId, toIndex) {
debugPrint('Move item from $fromIndex to $toIndex');
},
onMoveGroupItem: (groupId, fromIndex, toIndex) {
debugPrint('Move $groupId:$fromIndex to $groupId:$toIndex');
},
onMoveGroupItemToGroup: (fromGroupId, fromIndex, toGroupId, toIndex) {
debugPrint('Move $fromGroupId:$fromIndex to $toGroupId:$toIndex');
},
);
```
Provide an initial value of the board by initializing the `AppFlowyGroupData`. It represents a group data and contains list of items. Each item displayed in the group requires to implement the `AppFlowyGroupItem` class.
```dart
void initState() {
final group1 = AppFlowyGroupData(id: "To Do", items: [
TextItem("Card 1"),
TextItem("Card 2"),
]);
final group2 = AppFlowyGroupData(id: "In Progress", items: [
TextItem("Card 3"),
TextItem("Card 4"),
]);
final group3 = AppFlowyGroupData(id: "Done", items: []);
controller.addGroup(group1);
controller.addGroup(group2);
controller.addGroup(group3);
super.initState();
}
class TextItem extends AppFlowyGroupItem {
final String s;
TextItem(this.s);
@override
String get id => s;
}
```
Finally, return a `AppFlowyBoard` widget in the build method.
```dart
@override
Widget build(BuildContext context) {
return AppFlowyBoard(
controller: controller,
cardBuilder: (context, group, groupItem) {
final textItem = groupItem as TextItem;
return AppFlowyGroupCard(
key: ObjectKey(textItem),
child: Text(textItem.s),
);
},
groupConstraints: const BoxConstraints.tightFor(width: 240),
);
}
```
## Usage Example
To quickly grasp how it can be used, look at the /example/lib folder.
First, run main.dart to play with the demo.
Second, let's delve into multi_board_list_example.dart to understand a few key components:
* A Board widget is created via instantiating an `AppFlowyBoard` object.
* In the `AppFlowyBoard` object, you can find the `AppFlowyBoardController`, which is defined in board_data.dart, is fed with pre-populated mock data. It also contains callback functions to materialize future user data.
* Three builders: AppFlowyBoardHeaderBuilder, AppFlowyBoardFooterBuilder, AppFlowyBoardCardBuilder. See below image for what they are used for.
<p>
<img src="https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/app_flowy/packages/appflowy_board/example/gifs/appflowy_board_builders.jpg?raw=true" width="200" title="AppFlowyBoard">
</p>
## Glossary
Please refer to the API documentation.
## Contributing
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
Please look at [CONTRIBUTING.md](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/contributing-to-appflowy) for details.
## License
Distributed under the AGPLv3 License. See [LICENSE](https://github.com/AppFlowy-IO/AppFlowy-Docs/blob/main/LICENSE) for more information.
@@ -1,49 +0,0 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
.metadata
@@ -1,71 +0,0 @@
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.appflowy.board.example"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
@@ -1,8 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.appflowy.board.example">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
@@ -1,34 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.appflowy.board.example">
<application
android:label="example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
@@ -1,6 +0,0 @@
package com.appflowy.board.example
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}
@@ -1,8 +0,0 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.appflowy.board.example">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

@@ -1,481 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.board.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.board.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.board.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
@@ -1,59 +0,0 @@
import 'package:flutter/material.dart';
import 'single_board_list_example.dart';
import 'multi_board_list_example.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int _currentIndex = 0;
final _bottomNavigationColor = Colors.blue;
final List<Widget> _examples = [
const MultiBoardListExample(),
const SingleBoardListExample(),
];
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('AppFlowy Board'),
),
body: Container(color: Colors.white, child: _examples[_currentIndex]),
bottomNavigationBar: BottomNavigationBar(
fixedColor: _bottomNavigationColor,
showSelectedLabels: true,
showUnselectedLabels: false,
currentIndex: _currentIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.grid_on, color: _bottomNavigationColor),
label: "MultiColumn"),
BottomNavigationBarItem(
icon: Icon(Icons.grid_on, color: _bottomNavigationColor),
label: "SingleColumn"),
],
onTap: (int index) {
setState(() {
_currentIndex = index;
});
},
)),
);
}
}
@@ -1,198 +0,0 @@
import 'package:appflowy_board/appflowy_board.dart';
import 'package:flutter/material.dart';
class MultiBoardListExample extends StatefulWidget {
const MultiBoardListExample({Key? key}) : super(key: key);
@override
State<MultiBoardListExample> createState() => _MultiBoardListExampleState();
}
class _MultiBoardListExampleState extends State<MultiBoardListExample> {
final AppFlowyBoardController controller = AppFlowyBoardController(
onMoveGroup: (fromGroupId, fromIndex, toGroupId, toIndex) {
debugPrint('Move item from $fromIndex to $toIndex');
},
onMoveGroupItem: (groupId, fromIndex, toIndex) {
debugPrint('Move $groupId:$fromIndex to $groupId:$toIndex');
},
onMoveGroupItemToGroup: (fromGroupId, fromIndex, toGroupId, toIndex) {
debugPrint('Move $fromGroupId:$fromIndex to $toGroupId:$toIndex');
},
);
late AppFlowyBoardScrollController boardController;
@override
void initState() {
boardController = AppFlowyBoardScrollController();
final group1 = AppFlowyGroupData(id: "To Do", name: "To Do", items: [
TextItem("Card 1"),
TextItem("Card 2"),
RichTextItem(title: "Card 3", subtitle: 'Aug 1, 2020 4:05 PM'),
TextItem("Card 4"),
TextItem("Card 5"),
TextItem("Card 6"),
RichTextItem(title: "Card 7", subtitle: 'Aug 1, 2020 4:05 PM'),
RichTextItem(title: "Card 8", subtitle: 'Aug 1, 2020 4:05 PM'),
TextItem("Card 9"),
]);
final group2 = AppFlowyGroupData(
id: "In Progress",
name: "In Progress",
items: <AppFlowyGroupItem>[
RichTextItem(title: "Card 10", subtitle: 'Aug 1, 2020 4:05 PM'),
TextItem("Card 11"),
],
);
final group3 = AppFlowyGroupData(
id: "Done", name: "Done", items: <AppFlowyGroupItem>[]);
controller.addGroup(group1);
controller.addGroup(group2);
controller.addGroup(group3);
super.initState();
}
@override
Widget build(BuildContext context) {
final config = AppFlowyBoardConfig(
groupBackgroundColor: HexColor.fromHex('#F7F8FC'),
);
return AppFlowyBoard(
controller: controller,
cardBuilder: (context, group, groupItem) {
return AppFlowyGroupCard(
key: ValueKey(groupItem.id),
child: _buildCard(groupItem),
);
},
boardScrollController: boardController,
footerBuilder: (context, columnData) {
return AppFlowyGroupFooter(
icon: const Icon(Icons.add, size: 20),
title: const Text('New'),
height: 50,
margin: config.groupItemPadding,
onAddButtonClick: () {
boardController.scrollToBottom(columnData.id);
},
);
},
headerBuilder: (context, columnData) {
return AppFlowyGroupHeader(
icon: const Icon(Icons.lightbulb_circle),
title: SizedBox(
width: 60,
child: TextField(
controller: TextEditingController()
..text = columnData.headerData.groupName,
onSubmitted: (val) {
controller
.getGroupController(columnData.headerData.groupId)!
.updateGroupName(val);
},
),
),
addIcon: const Icon(Icons.add, size: 20),
moreIcon: const Icon(Icons.more_horiz, size: 20),
height: 50,
margin: config.groupItemPadding,
);
},
groupConstraints: const BoxConstraints.tightFor(width: 240),
config: config);
}
Widget _buildCard(AppFlowyGroupItem item) {
if (item is TextItem) {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
child: Text(item.s),
),
);
}
if (item is RichTextItem) {
return RichTextCard(item: item);
}
throw UnimplementedError();
}
}
class RichTextCard extends StatefulWidget {
final RichTextItem item;
const RichTextCard({
required this.item,
Key? key,
}) : super(key: key);
@override
State<RichTextCard> createState() => _RichTextCardState();
}
class _RichTextCardState extends State<RichTextCard> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.item.title,
style: const TextStyle(fontSize: 14),
textAlign: TextAlign.left,
),
const SizedBox(height: 10),
Text(
widget.item.subtitle,
style: const TextStyle(fontSize: 12, color: Colors.grey),
)
],
),
),
);
}
}
class TextItem extends AppFlowyGroupItem {
final String s;
TextItem(this.s);
@override
String get id => s;
}
class RichTextItem extends AppFlowyGroupItem {
final String title;
final String subtitle;
RichTextItem({required this.title, required this.subtitle});
@override
String get id => title;
}
extension HexColor on Color {
static Color fromHex(String hexString) {
final buffer = StringBuffer();
if (hexString.length == 6 || hexString.length == 7) buffer.write('ff');
buffer.write(hexString.replaceFirst('#', ''));
return Color(int.parse(buffer.toString(), radix: 16));
}
}
@@ -1,65 +0,0 @@
import 'package:flutter/material.dart';
import 'package:appflowy_board/appflowy_board.dart';
class SingleBoardListExample extends StatefulWidget {
const SingleBoardListExample({Key? key}) : super(key: key);
@override
State<SingleBoardListExample> createState() => _SingleBoardListExampleState();
}
class _SingleBoardListExampleState extends State<SingleBoardListExample> {
final AppFlowyBoardController boardData = AppFlowyBoardController();
@override
void initState() {
final column = AppFlowyGroupData(
id: "1",
name: "1",
items: [
TextItem("a"),
TextItem("b"),
TextItem("c"),
TextItem("d"),
],
);
boardData.addGroup(column);
super.initState();
}
@override
Widget build(BuildContext context) {
return AppFlowyBoard(
controller: boardData,
cardBuilder: (context, column, columnItem) {
return _RowWidget(
item: columnItem as TextItem, key: ObjectKey(columnItem));
},
);
}
}
class _RowWidget extends StatelessWidget {
final TextItem item;
const _RowWidget({Key? key, required this.item}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
key: ObjectKey(item),
height: 60,
color: Colors.green,
child: Center(child: Text(item.s)),
);
}
}
class TextItem extends AppFlowyGroupItem {
final String s;
TextItem(this.s);
@override
String get id => s;
}
@@ -1,138 +0,0 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
set(BINARY_NAME "example")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.appflowy.board.example")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(SET CMP0063 NEW)
# Load bundled libraries from the lib/ directory relative to the binary.
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Root filesystem for cross-building.
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endif()
# Define build configuration options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
# Compilation settings that should be applied to most targets.
#
# Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead
# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction()
# Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
# Define the application target. To change its name, change BINARY_NAME above,
# not the value here, or `flutter run` will no longer work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)
# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})
# Add dependency libraries. Add any application-specific dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)
# Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid
# people trying to run the unbundled copy, put it in a subdirectory instead of
# the default top-level location.
set_target_properties(${BINARY_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build
# directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
# Start with a clean build bundle directory every time.
install(CODE "
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
" COMPONENT Runtime)
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
install(FILES "${bundled_library}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endforeach(bundled_library)
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
@@ -1,11 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
void fl_register_plugins(FlPluginRegistry* registry) {
}
@@ -1,15 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter_linux/flutter_linux.h>
// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_
@@ -1,23 +0,0 @@
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)
@@ -1,10 +0,0 @@
//
// Generated file. Do not edit.
//
import FlutterMacOS
import Foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
}
@@ -1,14 +0,0 @@
// Application-level settings for the Runner target.
//
// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
// future. If not, the values below would default to using the project name when this becomes a
// 'flutter create' template.
// The application's name. By default this is also the title of the Flutter window.
PRODUCT_NAME = example
// The application's bundle identifier
PRODUCT_BUNDLE_IDENTIFIER = com.appflowy.board.example
// The copyright displayed in application information
PRODUCT_COPYRIGHT = Copyright © 2022 com.appflowy.board. All rights reserved.
Loaded 100 of 3199 files, more files were not shown because too many files have changed in this diff. Show more