Compare commits

...
269 Commits
Author SHA1 Message Date
Lucas.Xu ba957c0e81 fix: update the color of the selection menu item 2023-02-09 12:37:00 +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
1551 changed files with 49404 additions and 33947 deletions

No files matched your search

+3 -1
View File
@@ -15,6 +15,7 @@ on:
env:
CARGO_TERM_COLOR: always
FLUTTER_VERSION: "3.3.9"
jobs:
tests:
@@ -30,7 +31,7 @@ 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
@@ -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/app_flowy
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/app_flowy/**"
pull_request:
branches:
- "main"
- "release/*"
paths:
- "frontend/app_flowy/**"
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/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 AppFlowy tests
working-directory: frontend/app_flowy
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
+267 -194
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
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
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
build-for-windows:
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
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
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
- 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,232 @@ 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/app_flowy/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/app_flowy/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
-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
+1 -2
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
@@ -36,3 +34,4 @@ pubspec.lock
# ignore tool used for commit linting
.githooks/gitlint
.githooks/gitlint.exe
.fvm/
+105 -24
View File
@@ -1,58 +1,131 @@
# Release Notes
## Version 0.0.7 - 11/27/2022
## 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 +135,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 +143,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 +175,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 +188,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
+54 -50
View File
@@ -6,37 +6,20 @@
"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}"
},
{
// 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",
@@ -50,7 +33,7 @@
// - 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",
@@ -60,12 +43,38 @@
},
"cwd": "${workspaceRoot}/app_flowy"
},
{
"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}/app_flowy"
// },
{
// 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}/app_flowy"
},
{
// 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",
@@ -76,34 +85,29 @@
"cwd": "${workspaceRoot}/app_flowy"
},
{
"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/"
// },
]
}
+10
View File
@@ -9,6 +9,16 @@
"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,
+65 -37
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": {
@@ -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}"
@@ -185,6 +164,55 @@
"group": "build",
"problemMatcher": [],
"detail": "app_flowy"
},
{
"label": "AF: Tauri UI Dev",
"type": "shell",
"isBackground": true,
"command": "npm run dev",
"problemMatcher": ["$tsc"],
"options": {
"cwd": "${workspaceFolder}/appflowy_tauri"
}
},
{
"label": "AF: Tauri UI Build",
"type": "shell",
"command": "npm run build",
"problemMatcher": ["$tsc"],
"options": {
"cwd": "${workspaceFolder}/appflowy_tauri"
}
},
{
"label": "AF: Tauri Dev",
"type": "shell",
"command": "npm run tauri dev",
"problemMatcher": ["$tsc"],
"options": {
"cwd": "${workspaceFolder}/appflowy_tauri"
}
},
{
"label": "AF: Tauri Clean",
"type": "shell",
"command": "cargo make tauri_clean",
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": ["$tsc"]
},
{
"label": "AF: Tauri Clean + Dev",
"type": "shell",
"dependsOrder": "sequence",
"dependsOn": [
"AF: Tauri Clean",
"AF: Tauri UI Dev"
],
"options": {
"cwd": "${workspaceFolder}"
}
}
]
}
+23 -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]
@@ -17,17 +18,17 @@ on_error_task = "catch"
run_task = { name = ["restore-crate-type"] }
[env]
RUST_LOG = "info"
RUST_LOG = "debug"
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.0"
FLUTTER_DESKTOP_FEATURES = "flutter,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,8 +43,8 @@ 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 = "app_flowy/packages/appflowy_backend"
TAURI_BACKEND_SERVICE_PATH = "appflowy_tauri/src/services/backend"
# Test default config
TEST_CRATE_TYPE = "cdylib"
TEST_LIB_EXT = "dylib"
@@ -133,6 +134,7 @@ CRATE_TYPE = "cdylib"
FLUTTER_OUTPUT_DIR = "Debug"
LIB_EXT = "so"
LINUX_ARCH = "arm64"
FLUTTER_DESKTOP_FEATURES = "flutter,rev-sqlite,openssl_vendored"
[env.production-linux-aarch64]
BUILD_FLAG = "release"
@@ -143,6 +145,7 @@ FLUTTER_OUTPUT_DIR = "Release"
LIB_EXT = "so"
LINUX_ARCH = "arm64"
APP_ENVIRONMENT = "production"
FLUTTER_DESKTOP_FEATURES = "flutter,rev-sqlite,openssl_vendored"
[tasks.echo_env]
script = ['''
@@ -151,7 +154,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 +173,19 @@ BUILD_FLAG = "debug"
TARGET_OS = "android"
CRATE_TYPE = "cdylib"
FLUTTER_OUTPUT_DIR = "Debug"
FEATURES = "flutter,openssl_vendored"
FLUTTER_DESKTOP_FEATURES = "flutter,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 = "flutter,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 +197,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 +225,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 +237,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 +253,3 @@ script = ["""
cargo build -vv --features=dart
"""]
script_runner = "@shell"
@@ -0,0 +1,5 @@
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 14.0039C11.3137 14.0039 14 11.3176 14 8.00391C14 4.6902 11.3137 2.00391 8 2.00391C4.68629 2.00391 2 4.6902 2 8.00391C2 11.3176 4.68629 14.0039 8 14.0039Z" stroke="#333333" stroke-linecap="round" stroke-linejoin="round"/>
<rect x="7.5" y="7.00391" width="1" height="4" rx="0.5" fill="#333333"/>
<rect x="7.5" y="5.00391" width="1" height="1" rx="0.5" fill="#333333"/>
</svg>

After

Width:  |  Height:  |  Size: 482 B

@@ -0,0 +1,4 @@
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.01471 2.15012C7.62441 1.7981 8.37559 1.7981 8.98529 2.15012L12.5769 4.22372C13.1866 4.57573 13.5622 5.22628 13.5622 5.9303V10.0775C13.5622 10.7815 13.1866 11.4321 12.5769 11.7841L8.98529 13.8577C8.37559 14.2097 7.62441 14.2097 7.01471 13.8577L3.42312 11.7841C2.81341 11.4321 2.43782 10.7815 2.43782 10.0775V5.9303C2.43782 5.22628 2.81341 4.57573 3.42312 4.22372L7.01471 2.15012Z" stroke="#333333"/>
<circle cx="8" cy="8.00391" r="2.5" stroke="#333333"/>
</svg>

After

Width:  |  Height:  |  Size: 569 B

@@ -138,12 +138,16 @@
"open": "Obrir la configuració"
},
"appearance": {
"lightLabel": "Mode Clar",
"darkLabel": "Mode Fosc"
"themeMode": {
"label": "Theme Mode",
"light": "Mode Clar",
"dark": "Mode Fosc",
"system": "Adapt to System"
}
}
},
"sideBar": {
"openSidebar": "Open sidebar",
"closeSidebar": "Close sidebar"
}
}
}
+82 -9
View File
@@ -4,7 +4,7 @@
"welcomeText": "Welcome to @:appName",
"githubStarText": "Star on GitHub",
"subscribeNewsletterText": "Subscribe to Newsletter",
"letsGoButtonText": "Let's Go",
"letsGoButtonText": "Quick Start",
"title": "Title",
"signUp": {
"buttonText": "Sign Up",
@@ -40,6 +40,12 @@
"markdown": "Markdown",
"copyLink": "Copy Link"
},
"moreAction": {
"small": "small",
"medium": "medium",
"large": "large",
"fontSize": "Font Size"
},
"disclosureAction": {
"rename": "Rename",
"delete": "Delete",
@@ -90,14 +96,17 @@
"inlineCode": "Inline Code",
"quote": "Quote Block",
"header": "Header",
"highlight": "Highlight"
"highlight": "Highlight",
"color": "Color"
},
"tooltip": {
"lightMode": "Switch to Light mode",
"darkMode": "Switch to Dark mode",
"openAsPage": "Open as a Page",
"addNewRow": "Add a new row",
"openMenu": "Click to open menu"
"openMenu": "Click to open menu",
"viewDataBase": "View database",
"referencePage": "This {name} is referenced"
},
"sideBar": {
"closeSidebar": "Close side bar",
@@ -149,16 +158,44 @@
"appearance": "Appearance",
"language": "Language",
"user": "User",
"files": "Files",
"open": "Open Settings"
},
"appearance": {
"lightLabel": "Light Mode",
"darkLabel": "Dark Mode"
"themeMode": {
"label": "Theme Mode",
"light": "Light Mode",
"dark": "Dark Mode",
"system": "Adapt to System"
},
"theme": "Theme"
},
"files": {
"defaultLocation": "Where your data is stored now",
"doubleTapToCopy": "Double tap to copy the path",
"restoreLocation": "Restore to AppFlowy default path",
"customizeLocation": "Open another folder",
"restartApp": "Please restart app for the changes to take effect.",
"exportDatabase": "Export database",
"selectFiles": "Select the files that need to be export",
"createNewFolder": "Create a new folder",
"createNewFolderDesc": "Tell us where you want to store your data ...",
"open": "Open",
"openFolder": "Open an existing folder",
"openFolderDesc": "Read and write it to your existing AppFlowy folder ...",
"folderHintText": "folder name",
"location": "Creating a new folder",
"locationDesc": "Pick a name for your AppFlowy data folder",
"browser": "Browse",
"create": "Create",
"folderPath": "Path to store your folder",
"locationCannotBeEmpty": "Path cannot be empty"
}
},
"grid": {
"settings": {
"filter": "Filter",
"sort": "Sort",
"sortBy": "Sort by",
"Properties": "Properties",
"group": "Group",
@@ -191,6 +228,10 @@
"is": "is"
}
},
"checklistFilter": {
"isComplete": "is complete",
"isIncomplted": "is incomplete"
},
"singleSelectOptionFilter": {
"is": "Is",
"isNot": "Is not",
@@ -216,14 +257,15 @@
"singleSelectFieldName": "Select",
"multiSelectFieldName": "Multiselect",
"urlFieldName": "URL",
"numberFormat": " Number format",
"dateFormat": " Date format",
"includeTime": " Include time",
"checklistFieldName": "Checklist",
"numberFormat": "Number format",
"dateFormat": "Date format",
"includeTime": "Include time",
"dateFormatFriendly": "Month Day,Year",
"dateFormatISO": "Year-Month-Day",
"dateFormatLocal": "Month/Day/Year",
"dateFormatUS": "Year/Month/Day",
"timeFormat": " Time format",
"timeFormat": "Time format",
"invalidTimeFormat": "Invalid format",
"timeFormatTwelveHour": "12 hour",
"timeFormatTwentyFourHour": "24 hour",
@@ -234,6 +276,12 @@
"newColumn": "New column",
"deleteFieldPromptMessage": "Are you sure? This property will be deleted"
},
"sort": {
"ascending": "Ascending",
"descending": "Descending",
"deleteSort": "Delete sort",
"addSort": "Add sort"
},
"row": {
"duplicate": "Duplicate",
"delete": "Delete",
@@ -258,6 +306,9 @@
"panelTitle": "Select an option or create one",
"searchOption": "Search for an option"
},
"checklist": {
"panelTitle": "Add an item"
},
"menuName": "Grid"
},
"document": {
@@ -265,11 +316,33 @@
"date": {
"timeHintTextInTwelveHour": "01:00 PM",
"timeHintTextInTwentyFourHour": "13:00"
},
"slashMenu": {
"board": {
"selectABoardToLinkTo": "Select a Board to link to"
},
"grid": {
"selectAGridToLinkTo": "Select a Grid to link to"
}
},
"plugins": {
"referencedBoard": "Referenced Board",
"referencedGrid": "Referenced Grid"
}
},
"board": {
"column": {
"create_new_card": "New"
},
"menuName": "Board"
},
"calendar": {
"menuName": "Calendar",
"navigation": {
"today": "Today",
"jumpToday": "Jump to Today",
"previousMonth": "Previous Month",
"nextMonth": "Next Month"
}
}
}
@@ -90,7 +90,8 @@
"inlineCode": "Código embebido",
"quote": "Cita",
"header": "Título",
"highlight": "Resaltado"
"highlight": "Resaltado",
"color": "Color"
},
"tooltip": {
"lightMode": "Cambiar a modo Claro",
@@ -144,8 +145,12 @@
"open": "Abrir ajustes"
},
"appearance": {
"lightLabel": "Modo Claro",
"darkLabel": "Modo Oscuro"
"themeMode": {
"label": "Theme Mode",
"light": "Modo Claro",
"dark": "Modo Oscuro",
"system": "Adapt to System"
}
}
},
"grid": {
@@ -218,9 +223,9 @@
"openSidebar": "Abrir panel lateral",
"closeSidebar": "Cerrar panel lateral"
},
"board": {
"board": {
"column": {
"create_new_card": "Nuevo"
}
}
}
}
@@ -0,0 +1,333 @@
{
"appName": "AppFlowy",
"defaultUsername": "Ni",
"welcomeText": "Ongietorri @:appName -ra",
"githubStarText": "Izarra GitHub-en",
"subscribeNewsletterText": "Harpidetu buletinera",
"letsGoButtonText": "Hasi",
"title": "Izenburua",
"signUp": {
"buttonText": "Izena eman",
"title": "Izena eman @:appName -ra",
"getStartedText": "Hasi",
"emptyPasswordError": "Pasahitzak ezin du hutsik egon",
"repeatPasswordEmptyError": "Pasahitz errepikapenak ezin du hutsik egon",
"unmatchedPasswordError": "Pasahitz errepikapena ez da berdina",
"alreadyHaveAnAccount": "Kontu bat duzu jada?",
"emailHint": "Emaila",
"passwordHint": "Pasahitza",
"repeatPasswordHint": "Pasahitza errepikatu"
},
"signIn": {
"loginTitle": "Hasi saioa @:appName -n",
"loginButtonText": "Hasi saioa",
"buttonText": "Sartu",
"forgotPassword": "Pasahitza ahaztu duzu?",
"emailHint": "Emaila",
"passwordHint": "Pasahitza",
"dontHaveAnAccount": "Ez daukazu konturik?",
"repeatPasswordEmptyError": "Pasahitz errepikapenak ezin du hutsik egon",
"unmatchedPasswordError": "Pasahitz errepikapena ez da berdina"
},
"workspace": {
"create": "Lan-eremua",
"hint": "lan-eremua",
"notFoundError": "Lan-eremurik ez da aurkitu"
},
"shareAction": {
"buttonText": "Konpartitu",
"workInProgress": "Laister",
"markdown": "Markdown",
"copyLink": "Esteka kopiatu"
},
"moreAction": {
"small": "txikia",
"medium": "ertaina",
"large": "handia",
"fontSize": "Letra tamaina"
},
"disclosureAction": {
"rename": "Izena aldatu",
"delete": "Ezabatu",
"duplicate": "Duplikatu"
},
"blankPageTitle": "Orri zuria",
"newPageText": "Orri berria",
"trash": {
"text": "Zaborrontzia",
"restoreAll": "Guztia berreskuratu",
"deleteAll": "Guztia ezabatu",
"pageHeader": {
"fileName": "Fitxategi izena",
"lastModified": "Azken aldaketa",
"created": "Sortua"
}
},
"deletePagePrompt": {
"text": "Orri hau zaborrontzian dago",
"restore": "Orria berreskuratu",
"deletePermanent": "Betirako ezabatu"
},
"dialogCreatePageNameHint": "Orriaren izena",
"questionBubble": {
"whatsNew": "Ze berri?",
"help": "Laguntza",
"debug": {
"name": "Debug informazioa",
"success": "Debug informazioa kopiatu da!",
"fail": "Ezin izan da debug informazioa kopiatu"
}
},
"menuAppHeader": {
"addPageTooltip": "Gehitu orri bat",
"defaultNewPageName": "Izenbururik ez",
"renameDialog": "Izena aldatu"
},
"toolbar": {
"undo": "Desegin",
"redo": "Berregin",
"bold": "Lodia",
"italic": "Etzana",
"underline": "Azpimarratua",
"strike": "Markatua",
"numList": "Zembakidun zerrenda",
"bulletList": "Buletetako zerrenda",
"checkList": "Egiaztapen zerrenda",
"inlineCode": "Lerroko kodea",
"quote": "Aipamena",
"header": "Goiburua",
"highlight": "Nabarmendu",
"color": "Kolorea"
},
"tooltip": {
"lightMode": "Modu argira aldatu",
"darkMode": "Modu ilunera aldatu",
"openAsPage": "Orri gisa ireki",
"addNewRow": "Ilara berri bat gehitu",
"openMenu": "Egin klik menua irekitzeko"
},
"sideBar": {
"closeSidebar": "Alboko barra itxi",
"openSidebar": "Alboko barra ireki"
},
"notifications": {
"export": {
"markdown": "Oharra markdownera esportatuta",
"path": "Documents/flowy"
}
},
"contactsPage": {
"title": "Kontaktuak",
"whatsHappening": "Ze berri aste honetan?",
"addContact": "Kontaktua gehitu",
"editContact": "Kontaktua editatu"
},
"button": {
"OK": "OK",
"Cancel": "Ezteztatu",
"signIn": "Saioa hasi",
"signOut": "Saioa itxi",
"complete": "Burututa",
"save": "Gorde"
},
"label": {
"welcome": "Ongi etorri!",
"firstName": "Izena",
"middleName": "Bigarren izena",
"lastName": "Abizena",
"stepX": "{X}. pausoa"
},
"oAuth": {
"err": {
"failedTitle": "Ezin izan da kontura sartu.",
"failedMsg": "Mesedez, ziurtatu zure arakatzailean saioa hasteko prozesua amaitu duzula."
},
"google": {
"title": "GOOGLE SAIOA HASI",
"instruction1": "Zure Google Kontaktuak inportatzeko, zure web arakatzailea erabiliz aplikazio hau baimendu beharko duzu.",
"instruction2": "Kopiatu kode hau ikonoan klik eginez edo testua hautatuz:",
"instruction3": "Nabigatu zure web arakatzailean esteka honetara eta idatzi goiko kodea:",
"instruction4": "Sakatu beheko botoia erregistroa amaitzean:"
}
},
"settings": {
"title": "Ezarpenak",
"menu": {
"appearance": "Itxura",
"language": "Hizkuntza",
"user": "Erabiltzailea",
"files": "Fitxategiak",
"open": "Ezarpenak ireki"
},
"appearance": {
"themeMode": {
"label": "Itxura modua",
"light": "Modu argia",
"dark": "Modu iluna",
"system": "Zure sistemara moldatu"
},
"theme": "Itxura"
},
"files": {
"defaultLocation": "Non gordetzen diren zure datuak",
"doubleTapToCopy": "Sakatu birritan bidea kopiatzeko",
"restoreLocation": "Berrezarri AppFlowy-ren biden lehenetsira",
"customizeLocation": "Beste karpeta bat ireki",
"restartApp": "Mesedez, berrabiarazi aplikazioa aldaketak indarrean egon daitezen.",
"exportDatabase": "Datubasea exportatu",
"selectFiles": "Aukeratu exportatu nahi dituzun fitxategiak",
"createNewFolder": "Karpeta berri bat sortu",
"createNewFolderDesc": "Non nahi dituzu datuak gorde ...",
"open": "Oreki",
"openFolder": "Ireki karpeta bat",
"openFolderDesc": "Irakurri eta idatzi zure AppFlowy karpetan...",
"folderHintText": "karpetaren izena",
"location": "Karpeta berria sortzen",
"locationDesc": "Aukeratu izen bat AppFlowy datuen karpetarako",
"browser": "Bilatu",
"create": "Sortu",
"folderPath": "Zure karpeta gordetzeko bidea",
"locationCannotBeEmpty": "Bideak ezin du hutsa egon"
}
},
"grid": {
"settings": {
"filter": "Filtroa",
"sort": "Ordenatu",
"sortBy": "Ordenatu honekiko",
"Properties": "Propietateak",
"group": "Taldea",
"addFilter": "Gehitu iragazkia",
"deleteFilter": "Ezabatu iragazkia",
"filterBy": "Iragazi arabera...",
"typeAValue": "Idatzi balio bat..."
},
"textFilter": {
"contains": "Dauka",
"doesNotContain": "Ez dauka",
"endsWith": "Honez amaitzen da",
"startWith": "Honez hasten da",
"is": "da",
"isNot": "Ez da",
"isEmpty": "Hutsa dago",
"isNotEmpty": "Ez dago hutsik",
"choicechipPrefix": {
"isNot": "Ez da",
"startWith": "Honez hasten da",
"endWith": "Honez amaitzen da",
"isEmpty": "hutsik dago",
"isNotEmpty": "ez dago hutsik"
}
},
"checkboxFilter": {
"isChecked": "Egiaztatuta",
"isUnchecked": "Desmarkatua",
"choicechipPrefix": {
"da": "da"
}
},
"checklistFilter": {
"isComplete": "osatu da",
"isIncomplted": "osatu gabe dago"
},
"singleSelectOptionFilter": {
"is": "da",
"isNot": "Ez da",
"isEmpty": "Hutsa dago",
"isNotEmpty": "Ez dago hutsik"
},
"multiSelectOptionFilter": {
"contains": "Duen",
"doesNotContain": "Ez dauka",
"isEmpty": "Hutsa dago",
"isNotEmpty": "Ez dago hutsik"
},
"field": {
"hide": "Ezkutatu",
"insertLeft": "Txertatu ezkerrera",
"insertRight": "Txertatu eskuinera",
"duplicate": "Bikoiztu",
"delete": "Ezabatu",
"textFieldName": "Testua",
"checkboxFieldName": "Markatu laukia",
"dateFieldName": "Data",
"numberFieldName": "Zenbakiak",
"singleSelectFieldName": "Hautatu",
"multiSelectFieldName": "Multi-hautaketa",
"urlFieldName": "URL",
"checklistFieldName": "Kontrol zerrenda",
"numberFormat": "Zenbaki formatua",
"dateFormat": "Data formatua",
"includeTime": "Sartu ordua",
"dateFormatFriendly": "Hilabete Eguna, Urtea",
"dateFormatISO": "Urtea-Hilabetea-Eguna",
"dateFormatLocal": "Hilabetea/Eguna/Urtea",
"dateFormatUS": "Urtea/Hilabetea/Eguna",
"timeFormat": "Denboraren formatua",
"invalidTimeFormat": "Formatu baliogabea",
"timeFormatTwelveHour": "12 ordu",
"timeFormatTwentyFourHour": "24 ordu",
"addSelectOption": "Gehitu aukera bat",
"optionTitle": "Aukerak",
"addOption": "Gehitu aukera",
"editProperty": "Editatu propietatea",
"newColumn": "Zutabe berria",
"deleteFieldPromptMessage": "Ziur al zaude? Propietate hau ezabatu egingo da"
},
"sort": {
"ascending": "Gorarantz",
"descending": "Jaisten",
"deleteSort": "Ezabatu ordena",
"addSort": "Gehitu ordenatu"
},
"row": {
"duplicate": "Bikoiztu",
"delete": "Ezabatu",
"textPlaceholder": "Hutsik",
"copyProperty": "Propietatea arbelean kopiatu da",
"count": "Kontatu",
"newRow": "Errenkada berria"
},
"selectOption": {
"create": "Sortu",
"purpleColor": "Purple",
"pinkColor": "Rosa",
"lightPinkColor": "Arrosa argia",
"orangeColor": "Laranja",
"yellowColor": "Horia",
"limeColor": "Lima",
"greenColor": "Berdea",
"aquaColor": "Aqua",
"blueColor": "Urdina",
"deleteTag": "Ezabatu etiketa",
"colorPanelTitle": "Koloreak",
"panelTitle": "Hautatu aukera bat edo sortu bat",
"searchOption": "Aukera bat bilatu"
},
"checklist": {
"panelTitle": "Gehitu elementu bat"
},
"menuName": "Sareta"
},
"document": {
"menuName": "Dokumentua",
"data": {
"timeHintTextInTwelveHour": "01:00 PM",
"timeHintTextInTwentyFourHour": "13:00"
}
},
"board": {
"column": {
"create_new_card": "Berria"
}
},
"calendar": {
"menuName": "Egutegia",
"navigation": {
"today": "Gaur",
"jumpToday": "Gaurko egunera salto egin",
"previousMonth": "Aurreko hilabetea",
"nextMonth": "Hurrengo hilabetea"
}
}
}
@@ -138,12 +138,16 @@
"open": "Ouvrir les paramètres"
},
"appearance": {
"lightLabel": "Mode clair",
"darkLabel": "Mode sombre"
"themeMode": {
"label": "Theme Mode",
"light": "Mode clair",
"dark": "Mode sombre",
"system": "Adapt to System"
}
}
},
"sideBar": {
"openSidebar": "Open sidebar",
"closeSidebar": "Close sidebar"
}
}
}
@@ -152,8 +152,12 @@
"open": "Ouvrir les paramètres"
},
"appearance": {
"lightLabel": "Mode clair",
"darkLabel": "Mode sombre"
"themeMode": {
"label": "Theme Mode",
"light": "Mode clair",
"dark": "Mode sombre",
"system": "Adapt to System"
}
}
},
"grid": {
@@ -176,14 +180,14 @@
"singleSelectFieldName": "Sélectionner",
"multiSelectFieldName": "Multisélection",
"urlFieldName": "URL",
"numberFormat": " Format du nombre",
"dateFormat": " Format de la date",
"includeTime": " Inclure l'heure",
"numberFormat": "Format du nombre",
"dateFormat": "Format de la date",
"includeTime": "Inclure l'heure",
"dateFormatFriendly": "Mois Jour, Année",
"dateFormatISO": "Année-Mois-Jour",
"dateFormatLocal": "Mois/Jour/Année",
"dateFormatUS": "Année/Mois/Jour",
"timeFormat": " Format du temps",
"timeFormat": "Format du temps",
"invalidTimeFormat": "Format invalide",
"timeFormatTwelveHour": "12 heures",
"timeFormatTwentyFourHour": "24 heures",
@@ -138,12 +138,16 @@
"open": "Beállítások megnyitása"
},
"appearance": {
"lightLabel": "Világos mód",
"darkLabel": "Éjjeli mód"
"themeMode": {
"label": "Theme Mode",
"light": "Világos mód",
"dark": "Éjjeli mód",
"system": "Adapt to System"
}
}
},
"sideBar": {
"openSidebar": "Open sidebar",
"closeSidebar": "Close sidebar"
}
}
}
@@ -145,8 +145,12 @@
"open": "Buka Pengaturan"
},
"appearance": {
"lightLabel": "Mode Terang",
"darkLabel": "Mode Gelap"
"themeMode": {
"label": "Theme Mode",
"light": "Mode Terang",
"dark": "Mode Gelap",
"system": "Adapt to System"
}
}
},
"grid": {
@@ -168,14 +172,14 @@
"singleSelectFieldName": "seleksi",
"multiSelectFieldName": "Multi seleksi",
"urlFieldName": "URL",
"numberFormat": " Format angka",
"dateFormat": " Format tanggal",
"includeTime": " Sertakan waktu",
"numberFormat": "Format angka",
"dateFormat": "Format tanggal",
"includeTime": "Sertakan waktu",
"dateFormatFriendly": "Bulan Hari,Tahun",
"dateFormatISO": "Tahun-Bulan-Hari",
"dateFormatLocal": "Bulan/Hari/Tahun",
"dateFormatUS": "Tahun/Bulan/Hari",
"timeFormat": " Format waktu",
"timeFormat": "Format waktu",
"invalidTimeFormat": "Format yang tidak valid",
"timeFormatTwelveHour": "12 jam",
"timeFormatTwentyFourHour": "24 jam",
@@ -138,18 +138,22 @@
"open": "aprire le impostazioni"
},
"appearance": {
"lightLabel": "Modalità Chiara",
"darkLabel": "Modalità Scura"
"themeMode": {
"label": "Theme Mode",
"light": "Modalità Chiara",
"dark": "Modalità Scura",
"system": "Adapt to System"
}
}
},
"grid": {
"menuName":"Griglia"
"menuName": "Griglia"
},
"document":{
"menuName":"Documento"
"document": {
"menuName": "Documento"
},
"sideBar": {
"openSidebar": "Open sidebar",
"closeSidebar": "Close sidebar"
}
}
}
@@ -138,8 +138,12 @@
"open": "設定"
},
"appearance": {
"lightLabel": "ライトモード",
"darkLabel": "ダークモード"
"themeMode": {
"label": "Theme Mode",
"light": "ライトモード",
"dark": "ダークモード",
"system": "Adapt to System"
}
}
},
"grid": {
@@ -160,14 +164,14 @@
"numberFieldName": "数値",
"singleSelectFieldName": "単一選択",
"multiSelectFieldName": "複数選択",
"numberFormat": " 数値書式",
"dateFormat": " 日付書式",
"includeTime": " 時刻を含める",
"numberFormat": "数値書式",
"dateFormat": "日付書式",
"includeTime": "時刻を含める",
"dateFormatFriendly": "月 日,年",
"dateFormatISO": "年-月-日",
"dateFormatLocal": "月/日/年",
"dateFormatUS": "年/月/日",
"timeFormat": " 時刻書式",
"timeFormat": "時刻書式",
"timeFormatTwelveHour": "12 時間表記",
"timeFormatTwentyFourHour": "24 時間表記",
"addSelectOption": "選択候補追加",
+226 -227
View File
@@ -1,236 +1,235 @@
{
"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": "비밀번호 재입력"
"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": "브라우저에서 회원가입이 완료되었는지 확인해주세요."
},
"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": "가입 완료 후 아래 버튼을 눌러주세요:"
}
"google": {
"title": "GOOGLE SIGN-IN",
"instruction1": "구글 연락처를 가져오기 위해서 웹브라우저로 앱을 승인 해야 합니다.",
"instruction2": "아이콘을 클릭 또는 텍스트를 선택해서 이 코드를 클립보드로 복사하세요:",
"instruction3": "웹브라우저로 다음 링크로 가셔서 위 코드를 입력해주세요:",
"instruction4": "가입 완료 후 아래 버튼을 눌러주세요:"
}
},
"settings": {
"title": "설정",
"menu": {
"appearance": "화면",
"language": "언어",
"user": "사용자",
"open": "설정 열기"
},
"appearance": {
"lightLabel": "라이트 모드",
"darkLabel": "다크 모드"
}
},
"grid": {
"settings": {
"title": "설정",
"menu": {
"appearance": "화면",
"language": "언어",
"user": "사용자",
"open": "설정 열기"
},
"appearance": {
"lightLabel": "라이트 모드",
"darkLabel": "다크 모드"
}
"filter": "필터",
"sortBy": "정렬 기준",
"Properties": "속성",
"group": "그룹"
},
"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": "그리드"
"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": "해당 속성을 삭제 하시겠습니까?"
},
"document": {
"menuName": "도큐먼트",
"date": {
"timeHintTextInTwelveHour": "01:00 PM",
"timeHintTextInTwentyFourHour": "13:00"
}
"row": {
"duplicate": "복제",
"delete": "삭제",
"textPlaceholder": "비어있음",
"copyProperty": "속성이 클립보드로 복사됨",
"count": "개수",
"newRow": "행 추가"
},
"board": {
"column": {
"create_new_card": "추가"
}
"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": "추가"
}
}
}
@@ -138,12 +138,16 @@
"open": "Otwórz Ustawienia"
},
"appearance": {
"lightLabel": "Tryb Jasny",
"darkLabel": "Tryb Ciemny"
"themeMode": {
"label": "Theme Mode",
"light": "Tryb Jasny",
"dark": "Tryb Ciemny",
"system": "Adapt to System"
}
}
},
"sideBar": {
"openSidebar": "Open sidebar",
"closeSidebar": "Close sidebar"
}
}
}
@@ -152,8 +152,12 @@
"open": "Abrir as Configurações"
},
"appearance": {
"lightLabel": "Modo Claro",
"darkLabel": "Modo Escuro"
"themeMode": {
"label": "Theme Mode",
"light": "Modo Claro",
"dark": "Modo Escuro",
"system": "Adapt to System"
}
}
},
"grid": {
+107 -13
View File
@@ -40,6 +40,12 @@
"markdown": "Markdown",
"copyLink": "Скопировать ссылку"
},
"moreAction": {
"small": "маленький",
"medium": "средний",
"large": "большой",
"fontSize": "Размер шрифта"
},
"disclosureAction": {
"rename": "Переименовать",
"delete": "Удалить",
@@ -62,7 +68,7 @@
"restore": "Восстановить страницу",
"deletePermanent": "Удалить навсегда"
},
"dialogCreatePageNameHint": "Имя",
"dialogCreatePageNameHint": "Имя страницы",
"questionBubble": {
"whatsNew": "Что нового?",
"help": "Помощь",
@@ -90,10 +96,12 @@
"inlineCode": "Код",
"quote": "Цитата",
"header": "Заголовок",
"highlight": "Выделение"
"highlight": "Выделение",
"color": "Цвет"
},
"tooltip": {
"darkMode": "Переключиться в тёмную тему",
"lightMode": "Переключить на светлую тему",
"darkMode": "Переключить на тёмную тему",
"openAsPage": "Открыть как страницу",
"addNewRow": "Добавить новую строку",
"openMenu": "Открыть меню"
@@ -148,19 +156,91 @@
"appearance": "Внешний вид",
"language": "Язык",
"user": "Пользователь",
"files": "Файлы",
"open": "Открыть настройки"
},
"appearance": {
"lightLabel": "Светлая",
"darkLabel": "Тёмная"
"themeMode": {
"label": "Режим темы",
"light": "Светлая",
"dark": "Тёмная",
"system": "Системная"
},
"theme":"Тема"
},
"files": {
"defaultLocation": "Где хранить ваши данные",
"doubleTapToCopy": "Нажмите дважды, чтобы скопировать путь",
"restoreLocation": "Восстановить путь AppFlowy по умолчанию",
"customizeLocation": "Открыть другую папку",
"restartApp": "Пожалуйста, перезапустите приложение, чтобы изменения вступили в силу.",
"exportDatabase": "Экспорт базы данных",
"selectFiles": "Выбрать файлы, которые необходимо экспортировать",
"createNewFolder": "Создать новую папку",
"createNewFolderDesc": "Указать, где хранить свои данные ...",
"open": "Открыть",
"openFolder": "Открыть существующую папку",
"openFolderDesc": "Чтение и запись в существующую папку AppFlowy ...",
"folderHintText": "имя папки",
"location": "Создание новой папки",
"locationDesc": "Выбрать имя папки данных AppFlowy",
"browser": "Обзор",
"create": "Создать",
"folderPath": "Путь к вашей папке",
"locationCannotBeEmpty": "Путь не может быть пустым"
}
},
"grid": {
"settings": {
"filter": "Фильтр",
"sortBy": "Сортировать",
"sort": "Сортировать",
"sortBy": "Сортировать по",
"Properties": "Свойства",
"group": "Группировать"
"group": "Группировать",
"addFilter": "Добавить фильтр",
"deleteFilter": "Удалить фильтр",
"filterBy": "Фильтровать по...",
"typeAValue": "Введите значение..."
},
"textFilter": {
"contains": "Содержит",
"doesNotContain": "Не содержит",
"endsWith": "Заканчивается на",
"startWith": "Начинается с",
"is": "Является",
"isNot": "Не является",
"isEmpty": "Пусто",
"isNotEmpty": "Не пусто",
"choicechipPrefix": {
"isNot": "Не является",
"startWith": "Начинается с",
"endWith": "Заканчивается на",
"isEmpty": "пусто",
"isNotEmpty": "не пусто"
}
},
"checkboxFilter": {
"isChecked": "Отмечено",
"isUnchecked": "Не отмечено",
"choicechipPrefix": {
"is": "является"
}
},
"checklistFilter": {
"isComplete": "завершено",
"isIncomplted": "не завершено"
},
"singleSelectOptionFilter": {
"is": "Является",
"isNot": "Не является",
"isEmpty": "Пусто",
"isNotEmpty": "Не пусто"
},
"multiSelectOptionFilter": {
"contains": "Содержит",
"doesNotContain": "Не содержит",
"isEmpty": "Пусто",
"isNotEmpty": "Не пусто"
},
"field": {
"hide": "Скрыть",
@@ -175,14 +255,15 @@
"singleSelectFieldName": "Выбор",
"multiSelectFieldName": "Выбор нескольких",
"urlFieldName": "URL",
"numberFormat": " Формат числа",
"dateFormat": " Формат даты",
"includeTime": " Время",
"checklistFieldName": "Контрольный список",
"numberFormat": "Формат числа",
"dateFormat": "Формат даты",
"includeTime": "Время",
"dateFormatFriendly": "День Месяц, Год",
"dateFormatISO": "Год-Месяц-День",
"dateFormatLocal": "Месяц/День/Год",
"dateFormatUS": "Год/Месяц/День",
"timeFormat": " Форматировать время",
"timeFormat": "Форматировать время",
"invalidTimeFormat": "Неверный формат",
"timeFormatTwelveHour": "12 часов",
"timeFormatTwentyFourHour": "24 часа",
@@ -193,12 +274,19 @@
"newColumn": "Добавить колонку",
"deleteFieldPromptMessage": "Вы уверены? Свойство будет удалено"
},
"sort": {
"ascending": "По возрастанию",
"descending": "По убыванию",
"deleteSort": "Удалить сортировку",
"addSort": "Добавить сортировку"
},
"row": {
"duplicate": "Дублировать",
"delete": "Удалить",
"textPlaceholder": "Пусто",
"copyProperty": "Свойство скопировано",
"count": "Количество"
"count": "Количество",
"newRow": "Новая строка"
},
"selectOption": {
"create": "Создать",
@@ -216,6 +304,9 @@
"panelTitle": "Выберите или создайте вариант",
"searchOption": "Поиск"
},
"checklist": {
"panelTitle": "Добавить элемент"
},
"menuName": "Сетка"
},
"document": {
@@ -229,5 +320,8 @@
"column": {
"create_new_card": "Создать"
}
},
"calendar": {
"menuName": "Календарь"
}
}
}
+11 -7
View File
@@ -152,8 +152,12 @@
"open": "Öppna inställningarna"
},
"appearance": {
"lightLabel": "Ljust läge",
"darkLabel": "Mörkt läge"
"themeMode": {
"label": "Theme Mode",
"light": "Ljust läge",
"dark": "Mörkt läge",
"system": "Adapt to System"
}
}
},
"grid": {
@@ -176,14 +180,14 @@
"singleSelectFieldName": "Välj",
"multiSelectFieldName": "Välj flera",
"urlFieldName": "URL",
"numberFormat": " Sifferformat",
"dateFormat": " Datumformat",
"includeTime": " Inkludera tid",
"numberFormat": "Sifferformat",
"dateFormat": "Datumformat",
"includeTime": "Inkludera tid",
"dateFormatFriendly": "Månad Dag,År",
"dateFormatISO": "År-Månad-Dag",
"dateFormatLocal": "Månad/Dag/År",
"dateFormatUS": "År/Månad/Dag",
"timeFormat": " Tidsformat",
"timeFormat": "Tidsformat",
"invalidTimeFormat": "Ogiltigt format",
"timeFormatTwelveHour": "12-timmars",
"timeFormatTwentyFourHour": "24-timmars",
@@ -232,4 +236,4 @@
"create_new_card": "Nytt"
}
}
}
}
@@ -138,12 +138,16 @@
"open": "Ayarları Aç"
},
"appearance": {
"lightLabel": "Aydınlık Mod",
"darkLabel": "Karanlık Mod"
"themeMode": {
"label": "Theme Mode",
"light": "Aydınlık Mod",
"dark": "Karanlık Mod",
"system": "Adapt to System"
}
}
},
"sideBar": {
"openSidebar": "Open sidebar",
"closeSidebar": "Close sidebar"
}
}
}
@@ -152,8 +152,12 @@
"open": "打开设置"
},
"appearance": {
"lightLabel": "日间模式",
"darkLabel": "夜间模式"
"themeMode": {
"label": "Theme Mode",
"light": "日间模式",
"dark": "夜间模式",
"system": "Adapt to System"
}
}
},
"grid": {
@@ -176,14 +180,14 @@
"singleSelectFieldName": "单项选择器",
"multiSelectFieldName": "多项选择器",
"urlFieldName": "链接",
"numberFormat": " 数字格式",
"dateFormat": " 日期格式",
"includeTime": " 包含时间",
"numberFormat": "数字格式",
"dateFormat": "日期格式",
"includeTime": "包含时间",
"dateFormatFriendly": "月 日,年",
"dateFormatISO": "年-月-日",
"dateFormatLocal": "月/日/年",
"dateFormatUS": "年/月/日",
"timeFormat": " 时间格式",
"timeFormat": "时间格式",
"invalidTimeFormat": "时间格式错误",
"timeFormatTwelveHour": "12小时制",
"timeFormatTwentyFourHour": "24小时制",
@@ -145,8 +145,12 @@
"open": "開啟設定"
},
"appearance": {
"lightLabel": "亮色模式",
"darkLabel": "暗色模式"
"themeMode": {
"label": "Theme Mode",
"light": "亮色模式",
"dark": "暗色模式",
"system": "Adapt to System"
}
}
},
"grid": {
@@ -168,14 +172,14 @@
"singleSelectFieldName": "單選",
"multiSelectFieldName": "多選",
"urlFieldName": "網址",
"numberFormat": " 數字格式",
"dateFormat": " 日期格式",
"includeTime": " 包含時間",
"numberFormat": "數字格式",
"dateFormat": "日期格式",
"includeTime": "包含時間",
"dateFormatFriendly": "月 日,年",
"dateFormatISO": "年-月-日",
"dateFormatLocal": "月/日/年",
"dateFormatUS": "年/月/日",
"timeFormat": " 時間格式",
"timeFormat": "時間格式",
"invalidTimeFormat": "格式無效",
"timeFormatTwelveHour": "12 小時",
"timeFormatTwentyFourHour": "24 小時",
@@ -0,0 +1,161 @@
import 'package:app_flowy/user/presentation/folder/folder_widget.dart';
import 'package:flowy_infra_ui/style_widget/text_field.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'util/mock/mock_file_picker.dart';
import 'util/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('customize the folder path', () {
const location = 'appflowy';
setUp(() async {
await TestFolder.cleanTestLocation(location);
await TestFolder.setTestLocation(location);
});
tearDown(() async {
await TestFolder.cleanTestLocation(location);
});
tearDownAll(() async {
await TestFolder.cleanTestLocation(null);
});
testWidgets(
'customize folder name and path when launching app in first time',
(tester) async {
const folderName = 'appflowy';
await TestFolder.cleanTestLocation(folderName);
await tester.initializeAppFlowy();
// Click create button
await tester.tapCreateButton();
// Set directory
final cfw = find.byType(CreateFolderWidget);
expect(cfw, findsOneWidget);
final state = tester.state(cfw) as CreateFolderWidgetState;
final dir = await TestFolder.testLocation(null);
state.directory = dir.path;
// input folder name
final ftf = find.byType(FlowyTextField);
expect(ftf, findsOneWidget);
await tester.enterText(ftf, 'appflowy');
// Click create button again
await tester.tapCreateButton();
await tester.expectToSeeWelcomePage();
await TestFolder.cleanTestLocation(folderName);
});
testWidgets('open a new folder when launching app in first time',
(tester) async {
const folderName = 'appflowy';
await TestFolder.cleanTestLocation(folderName);
await TestFolder.setTestLocation(folderName);
await tester.initializeAppFlowy();
// tap open button
await mockGetDirectoryPath(folderName);
await tester.tapOpenFolderButton();
await tester.wait(1000);
await tester.expectToSeeWelcomePage();
await TestFolder.cleanTestLocation(folderName);
});
testWidgets('switch to B from A, then switch to A again', (tester) async {
const String userA = 'userA';
const String userB = 'userB';
await TestFolder.cleanTestLocation(userA);
await TestFolder.setTestLocation(userA);
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.expectToSeeWelcomePage();
// swith to user B
{
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
await tester.enterUserName(userA);
await tester.openSettingsPage(SettingsPage.files);
await tester.pumpAndSettle();
// mock the file_picker result
await mockGetDirectoryPath(userB);
await tester.tapCustomLocationButton();
await tester.pumpAndSettle();
await tester.expectToSeeWelcomePage();
}
// switch to the userA
{
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
await tester.enterUserName(userB);
await tester.openSettingsPage(SettingsPage.files);
await tester.pumpAndSettle();
// mock the file_picker result
await mockGetDirectoryPath(userA);
await tester.tapCustomLocationButton();
await tester.pumpAndSettle();
await tester.expectToSeeWelcomePage();
expect(find.textContaining(userA), findsOneWidget);
}
// swith to the userB again
{
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.files);
await tester.pumpAndSettle();
// mock the file_picker result
await mockGetDirectoryPath(userB);
await tester.tapCustomLocationButton();
await tester.pumpAndSettle();
await tester.expectToSeeWelcomePage();
expect(find.textContaining(userB), findsOneWidget);
}
await TestFolder.cleanTestLocation(userA);
await TestFolder.cleanTestLocation(userB);
});
testWidgets('reset to default location', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
// home and readme document
await tester.expectToSeeWelcomePage();
// open settings and restore the location
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.files);
await tester.restoreLocation();
expect(
await TestFolder.defaultDevelopmentLocation(),
await TestFolder.currentLocation(),
);
});
});
}
@@ -0,0 +1,114 @@
import 'dart:io';
import 'package:app_flowy/main.dart' as app;
import 'package:app_flowy/startup/tasks/prelude.dart';
import 'package:app_flowy/workspace/application/settings/settings_location_cubit.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
class TestFolder {
/// Location / Path
/// Set a given AppFlowy data storage location under test environment.
///
/// To pass null means clear the location.
///
/// The file_picker is a system component and can't be tapped, so using logic instead of tapping.
///
static Future<void> setTestLocation(String? name) async {
final location = await testLocation(name);
SharedPreferences.setMockInitialValues({
kSettingsLocationDefaultLocation: location.path,
});
return;
}
/// Clean the location.
static Future<void> cleanTestLocation(String? name) async {
final dir = await testLocation(name);
await dir.delete(recursive: true);
return;
}
/// Get current using location.
static Future<String> currentLocation() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString(kSettingsLocationDefaultLocation)!;
}
/// Get default location under development environment.
static Future<String> defaultDevelopmentLocation() async {
final dir = await appFlowyDocumentDirectory();
return dir.path;
}
/// Get default location under test environment.
static Future<Directory> testLocation(String? name) async {
final dir = await getApplicationDocumentsDirectory();
var path = '${dir.path}/flowy_test';
if (name != null) {
path += '/$name';
}
return Directory(path).create(recursive: true);
}
}
extension AppFlowyTestBase on WidgetTester {
Future<void> initializeAppFlowy() async {
const MethodChannel('hotkey_manager')
.setMockMethodCallHandler((MethodCall methodCall) async {
if (methodCall.method == 'unregisterAll') {
// do nothing
}
});
await app.main();
await wait(3000);
await pumpAndSettle(const Duration(seconds: 2));
return;
}
Future<void> tapButton(
Finder finder, {
int? pointer,
int buttons = kPrimaryButton,
bool warnIfMissed = true,
int milliseconds = 500,
}) async {
await tap(finder);
await pumpAndSettle(Duration(milliseconds: milliseconds));
return;
}
Future<void> tapButtonWithName(
String tr, {
int milliseconds = 500,
}) async {
final button = find.text(tr);
await tapButton(
button,
milliseconds: milliseconds,
);
return;
}
Future<void> tapButtonWithTooltip(
String tr, {
int milliseconds = 500,
}) async {
final button = find.byTooltip(tr);
await tapButton(
button,
milliseconds: milliseconds,
);
return;
}
Future<void> wait(int milliseconds) async {
await pumpAndSettle(Duration(milliseconds: milliseconds));
return;
}
}
@@ -0,0 +1,23 @@
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_test/flutter_test.dart';
import 'base.dart';
extension AppFlowyLaunch on WidgetTester {
Future<void> tapGoButton() async {
await tapButtonWithName(LocaleKeys.letsGoButtonText.tr());
return;
}
Future<void> tapCreateButton() async {
await tapButtonWithName(LocaleKeys.settings_files_create.tr());
return;
}
Future<void> expectToSeeWelcomePage() async {
expect(find.byType(HomeStack), findsOneWidget);
expect(find.textContaining('Read me'), findsNWidgets(2));
}
}
@@ -0,0 +1,29 @@
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/util/file_picker/file_picker_impl.dart';
import 'package:app_flowy/util/file_picker/file_picker_service.dart';
import '../util.dart';
class MockFilePicker extends FilePicker {
MockFilePicker({
required this.mockPath,
});
final String mockPath;
@override
Future<String?> getDirectoryPath({String? title}) {
return Future.value(mockPath);
}
}
Future<void> mockGetDirectoryPath(String? name) async {
final dir = await TestFolder.testLocation(name);
getIt.unregister<FilePickerService>();
getIt.registerFactory<FilePickerService>(
() => MockFilePicker(
mockPath: dir.path,
),
);
return;
}
@@ -0,0 +1,84 @@
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/workspace/presentation/settings/settings_dialog.dart';
import 'package:app_flowy/workspace/presentation/settings/widgets/settings_user_view.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_test/flutter_test.dart';
import 'base.dart';
enum SettingsPage {
appearance,
language,
files,
user,
}
extension on SettingsPage {
String get name {
switch (this) {
case SettingsPage.appearance:
return LocaleKeys.settings_menu_appearance.tr();
case SettingsPage.language:
return LocaleKeys.settings_menu_language.tr();
case SettingsPage.files:
return LocaleKeys.settings_menu_files.tr();
case SettingsPage.user:
return LocaleKeys.settings_menu_user.tr();
}
}
}
extension AppFlowySettings on WidgetTester {
/// Open settings page
Future<void> openSettings() async {
final settingsButton = find.byTooltip(LocaleKeys.settings_menu_open.tr());
expect(settingsButton, findsOneWidget);
await tapButton(settingsButton);
final settingsDialog = find.byType(SettingsDialog);
expect(settingsDialog, findsOneWidget);
return;
}
/// Open the page taht insides the settings page
Future<void> openSettingsPage(SettingsPage page) async {
final button = find.text(page.name, findRichText: true);
expect(button, findsOneWidget);
await tapButton(button);
return;
}
/// Restore the AppFlowy data storage location
Future<void> restoreLocation() async {
final buton =
find.byTooltip(LocaleKeys.settings_files_restoreLocation.tr());
expect(buton, findsOneWidget);
await tapButton(buton);
return;
}
Future<void> tapOpenFolderButton() async {
final buton = find.text(LocaleKeys.settings_files_open.tr());
expect(buton, findsOneWidget);
await tapButton(buton);
return;
}
Future<void> tapCustomLocationButton() async {
final buton =
find.byTooltip(LocaleKeys.settings_files_customizeLocation.tr());
expect(buton, findsOneWidget);
await tapButton(buton);
return;
}
/// Enter user name
Future<void> enterUserName(String name) async {
final uni = find.byType(UserNameInput);
expect(uni, findsOneWidget);
await tap(uni);
await enterText(uni, name);
await wait(300); //
await testTextInput.receiveAction(TextInputAction.done);
await pumpAndSettle();
}
}
@@ -0,0 +1,3 @@
export 'base.dart';
export 'launch.dart';
export 'settings.dart';
+5 -5
View File
@@ -3,7 +3,7 @@ PODS:
- Flutter
- flowy_infra_ui (0.0.1):
- Flutter
- flowy_sdk (0.0.1):
- appflowy_backend (0.0.1):
- Flutter
- Flutter (1.0.0)
- flutter_inappwebview (0.0.1):
@@ -28,7 +28,7 @@ PODS:
DEPENDENCIES:
- flowy_editor (from `.symlinks/plugins/flowy_editor/ios`)
- flowy_infra_ui (from `.symlinks/plugins/flowy_infra_ui/ios`)
- flowy_sdk (from `.symlinks/plugins/flowy_sdk/ios`)
- appflowy_backend (from `.symlinks/plugins/appflowy_backend/ios`)
- Flutter (from `Flutter`)
- flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`)
- flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
@@ -46,8 +46,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flowy_editor/ios"
flowy_infra_ui:
:path: ".symlinks/plugins/flowy_infra_ui/ios"
flowy_sdk:
:path: ".symlinks/plugins/flowy_sdk/ios"
appflowy_backend:
:path: ".symlinks/plugins/appflowy_backend/ios"
Flutter:
:path: Flutter
flutter_inappwebview:
@@ -66,7 +66,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
flowy_editor: bf8d58894ddb03453bd4d8521c57267ad638b837
flowy_infra_ui: 146c88346fd55d2ee6a41ae35059a5bf095cfbb3
flowy_sdk: c416222c639e678828776789bf0c1a1d0d59df3c
appflowy_backend: c416222c639e678828776789bf0c1a1d0d59df3c
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
@@ -1,18 +1,23 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-notification/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 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/notification.pb.dart';
import 'package:appflowy_backend/rust_stream.dart';
import 'notification_helper.dart';
// Folder
typedef FolderNotificationCallback = void Function(FolderNotification, Either<Uint8List, FlowyError>);
typedef FolderNotificationCallback = void Function(
FolderNotification,
Either<Uint8List, FlowyError>,
);
class FolderNotificationParser extends NotificationParser<FolderNotification, FlowyError> {
FolderNotificationParser({String? id, required FolderNotificationCallback callback})
class FolderNotificationParser
extends NotificationParser<FolderNotification, FlowyError> {
FolderNotificationParser(
{String? id, required FolderNotificationCallback callback})
: super(
id: id,
callback: callback,
@@ -21,15 +26,24 @@ class FolderNotificationParser extends NotificationParser<FolderNotification, Fl
);
}
typedef FolderNotificationHandler = Function(FolderNotification ty, Either<Uint8List, FlowyError> result);
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));
FolderNotificationListener({
required String objectId,
required FolderNotificationHandler handler,
}) : _parser = FolderNotificationParser(
id: objectId,
callback: handler,
) {
_subscription =
RustStreamReceiver.listen((observable) => _parser?.parse(observable));
}
Future<void> stop() async {
@@ -1,39 +1,42 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-notification/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 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/notification.pb.dart';
import 'package:appflowy_backend/rust_stream.dart';
import 'notification_helper.dart';
// GridPB
typedef GridNotificationCallback = void Function(
GridDartNotification, Either<Uint8List, FlowyError>);
// DatabasePB
typedef DatabaseNotificationCallback = void Function(
DatabaseNotification,
Either<Uint8List, FlowyError>,
);
class GridNotificationParser
extends NotificationParser<GridDartNotification, FlowyError> {
GridNotificationParser(
{String? id, required GridNotificationCallback callback})
class DatabaseNotificationParser
extends NotificationParser<DatabaseNotification, FlowyError> {
DatabaseNotificationParser(
{String? id, required DatabaseNotificationCallback callback})
: super(
id: id,
callback: callback,
tyParser: (ty) => GridDartNotification.valueOf(ty),
tyParser: (ty) => DatabaseNotification.valueOf(ty),
errorParser: (bytes) => FlowyError.fromBuffer(bytes),
);
}
typedef GridNotificationHandler = Function(
GridDartNotification ty, Either<Uint8List, FlowyError> result);
typedef DatabaseNotificationHandler = Function(
DatabaseNotification ty, Either<Uint8List, FlowyError> result);
class GridNotificationListener {
class DatabaseNotificationListener {
StreamSubscription<SubscribeObject>? _subscription;
GridNotificationParser? _parser;
DatabaseNotificationParser? _parser;
GridNotificationListener(
{required String objectId, required GridNotificationHandler handler})
: _parser = GridNotificationParser(id: objectId, callback: handler) {
DatabaseNotificationListener({
required String objectId,
required DatabaseNotificationHandler handler,
}) : _parser = DatabaseNotificationParser(id: objectId, callback: handler) {
_subscription =
RustStreamReceiver.listen((observable) => _parser?.parse(observable));
}
@@ -1,9 +1,9 @@
import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-net/network_state.pb.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-net/network_state.pb.dart';
import 'package:flutter/services.dart';
class NetworkListener {
@@ -35,21 +35,21 @@ class NetworkListener {
final networkType = () {
switch (result) {
case ConnectivityResult.wifi:
return NetworkType.Wifi;
return NetworkTypePB.Wifi;
case ConnectivityResult.ethernet:
return NetworkType.Ethernet;
return NetworkTypePB.Ethernet;
case ConnectivityResult.mobile:
return NetworkType.Cell;
return NetworkTypePB.Cell;
case ConnectivityResult.bluetooth:
return NetworkType.Bluetooth;
return NetworkTypePB.Bluetooth;
case ConnectivityResult.vpn:
return NetworkType.VPN;
return NetworkTypePB.VPN;
case ConnectivityResult.none:
return NetworkType.UnknownNetworkType;
return NetworkTypePB.Unknown;
}
}();
Log.info("Network type: $networkType");
final state = NetworkState.create()..ty = networkType;
final state = NetworkStatePB.create()..ty = networkType;
NetworkEventUpdateNetworkType(state).send().then((result) {
result.fold(
(l) {},
@@ -1,5 +1,5 @@
import 'dart:typed_data';
import 'package:flowy_sdk/protobuf/dart-notify/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-notification/protobuf.dart';
import 'package:dartz/dartz.dart';
class NotificationParser<T, E> {
@@ -9,7 +9,11 @@ class NotificationParser<T, E> {
T? Function(int) tyParser;
E Function(Uint8List) errorParser;
NotificationParser({this.id, required this.callback, required this.errorParser, required this.tyParser});
NotificationParser(
{this.id,
required this.callback,
required this.errorParser,
required this.tyParser});
void parse(SubscribeObject subject) {
if (id != null) {
if (subject.id != id) {
@@ -1,18 +1,23 @@
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:appflowy_backend/protobuf/flowy-notification/protobuf.dart';
import 'package:appflowy_backend/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 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/rust_stream.dart';
import 'notification_helper.dart';
// User
typedef UserNotificationCallback = void Function(UserNotification, Either<Uint8List, FlowyError>);
typedef UserNotificationCallback = void Function(
UserNotification,
Either<Uint8List, FlowyError>,
);
class UserNotificationParser extends NotificationParser<UserNotification, FlowyError> {
UserNotificationParser({required String id, required UserNotificationCallback callback})
class UserNotificationParser
extends NotificationParser<UserNotification, FlowyError> {
UserNotificationParser(
{required String id, required UserNotificationCallback callback})
: super(
id: id,
callback: callback,
@@ -21,15 +26,19 @@ class UserNotificationParser extends NotificationParser<UserNotification, FlowyE
);
}
typedef UserNotificationHandler = Function(UserNotification ty, Either<Uint8List, FlowyError> result);
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));
UserNotificationListener({
required String objectId,
required UserNotificationHandler handler,
}) : _parser = UserNotificationParser(id: objectId, callback: handler) {
_subscription =
RustStreamReceiver.listen((observable) => _parser?.parse(observable));
}
Future<void> stop() async {
+10 -6
View File
@@ -1,17 +1,21 @@
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';
import 'package:hotkey_manager/hotkey_manager.dart';
import 'startup/launch_configuration.dart';
import 'startup/startup.dart';
import 'user/presentation/splash_screen.dart';
class FlowyApp implements EntryPoint {
@override
Widget create() {
return const SplashScreen();
Widget create(LaunchConfiguration config) {
return SplashScreen(
autoRegister: config.autoRegistrationSupported,
);
}
}
void main() async {
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized();
@@ -1,17 +1,16 @@
import 'dart:async';
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/row/row_cache.dart';
import 'package:app_flowy/plugins/grid/application/row/row_service.dart';
import 'package:appflowy_board/appflowy_board.dart';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.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/protobuf.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -29,10 +28,10 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
GridFieldController get fieldController =>
_gridDataController.fieldController;
String get gridId => _gridDataController.gridId;
String get databaseId => _gridDataController.viewId;
BoardBloc({required ViewPB view})
: _rowService = MoveRowFFIService(gridId: view.id),
: _rowService = MoveRowFFIService(viewId: view.id),
_gridDataController = BoardDataController(view: view),
super(BoardState.initial(view.id)) {
boardController = AppFlowyBoardController(
@@ -117,7 +116,7 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
emit(state.copyWith(editingRow: none()));
});
},
didReceiveGridUpdate: (GridPB grid) {
didReceiveGridUpdate: (DatabasePB grid) {
emit(state.copyWith(grid: Some(grid)));
},
didReceiveError: (FlowyError error) {
@@ -187,50 +186,26 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
return super.close();
}
void initializeGroups(List<GroupPB> groupsData) {
void initializeGroups(List<GroupPB> groups) {
for (var controller in groupControllers.values) {
controller.dispose();
}
groupControllers.clear();
boardController.clear();
//
List<AppFlowyGroupData> groups = groupsData
boardController.addGroups(groups
.where((group) => fieldController.getField(group.fieldId) != null)
.map((group) {
return AppFlowyGroupData(
id: group.groupId,
name: group.desc,
items: _buildGroupItems(group),
customData: GroupData(
group: group,
fieldInfo: fieldController.getField(group.fieldId)!,
),
);
}).toList();
boardController.addGroups(groups);
.map((group) => initializeGroupData(group))
.toList());
for (final group in groupsData) {
final delegate = GroupControllerDelegateImpl(
controller: boardController,
fieldController: fieldController,
onNewColumnItem: (groupId, row, index) {
add(BoardEvent.didCreateRow(group, row, index));
},
);
final controller = GroupController(
gridId: state.gridId,
group: group,
delegate: delegate,
);
controller.startListening();
for (final group in groups) {
final controller = initializeGroupController(group);
groupControllers[controller.group.groupId] = (controller);
}
}
GridRowCache? getRowCache(String blockId) {
final GridBlockCache? blockCache = _gridDataController.blocks[blockId];
return blockCache?.rowCache;
return _gridDataController.rowCache;
}
void _startListening() {
@@ -247,11 +222,15 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
},
onDeletedGroup: (groupIds) {
if (isClosed) return;
//
boardController.removeGroups(groupIds);
},
onInsertedGroup: (insertedGroups) {
onInsertedGroup: (insertedGroup) {
if (isClosed) return;
//
final group = insertedGroup.group;
final newGroup = initializeGroupData(group);
final controller = initializeGroupController(group);
groupControllers[controller.group.groupId] = (controller);
boardController.addGroup(newGroup);
},
onUpdatedGroup: (updatedGroups) {
if (isClosed) return;
@@ -296,6 +275,35 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
),
);
}
GroupController initializeGroupController(GroupPB group) {
final delegate = GroupControllerDelegateImpl(
controller: boardController,
fieldController: fieldController,
onNewColumnItem: (groupId, row, index) {
add(BoardEvent.didCreateRow(group, row, index));
},
);
final controller = GroupController(
databaseId: state.databaseId,
group: group,
delegate: delegate,
);
controller.startListening();
return controller;
}
AppFlowyGroupData initializeGroupData(GroupPB group) {
return AppFlowyGroupData(
id: group.groupId,
name: group.desc,
items: _buildGroupItems(group),
customData: GroupData(
group: group,
fieldInfo: fieldController.getField(group.fieldId)!,
),
);
}
}
@freezed
@@ -315,7 +323,7 @@ class BoardEvent with _$BoardEvent {
const factory BoardEvent.endEditingRow(String rowId) = _EndEditRow;
const factory BoardEvent.didReceiveError(FlowyError error) = _DidReceiveError;
const factory BoardEvent.didReceiveGridUpdate(
GridPB grid,
DatabasePB grid,
) = _DidReceiveGridUpdate;
const factory BoardEvent.didReceiveGroups(List<GroupPB> groups) =
_DidReceiveGroups;
@@ -324,17 +332,17 @@ class BoardEvent with _$BoardEvent {
@freezed
class BoardState with _$BoardState {
const factory BoardState({
required String gridId,
required Option<GridPB> grid,
required String databaseId,
required Option<DatabasePB> grid,
required List<String> groupIds,
required Option<BoardEditingRow> editingRow,
required GridLoadingState loadingState,
required Option<FlowyError> noneOrError,
}) = _BoardState;
factory BoardState.initial(String gridId) => BoardState(
factory BoardState.initial(String databaseId) => BoardState(
grid: none(),
gridId: gridId,
databaseId: databaseId,
groupIds: [],
editingRow: none(),
noneOrError: none(),
@@ -1,23 +1,23 @@
import 'dart:collection';
import 'package:app_flowy/plugins/grid/application/block/block_cache.dart';
import 'package:app_flowy/plugins/grid/application/view/grid_view_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 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'dart:async';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
import 'board_listener.dart';
typedef OnFieldsChanged = void Function(UnmodifiableListView<FieldInfo>);
typedef OnGridChanged = void Function(GridPB);
typedef OnGridChanged = void Function(DatabasePB);
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 OnInsertedGroup = void Function(InsertedGroupPB);
typedef OnResetGroups = void Function(List<GroupPB>);
typedef OnRowsChanged = void Function(
@@ -27,15 +27,11 @@ typedef OnRowsChanged = void Function(
typedef OnError = void Function(FlowyError);
class BoardDataController {
final String gridId;
final GridFFIService _gridFFIService;
final String viewId;
final DatabaseFFIService _databaseFFIService;
final GridFieldController fieldController;
final BoardListener _listener;
// key: the block id
final LinkedHashMap<String, GridBlockCache> _blocks;
UnmodifiableMapView<String, GridBlockCache> get blocks =>
UnmodifiableMapView(_blocks);
late DatabaseViewCache _viewCache;
OnFieldsChanged? _onFieldsChanged;
OnGridChanged? _onGridChanged;
@@ -43,21 +39,23 @@ class BoardDataController {
OnRowsChanged? _onRowsChanged;
OnError? _onError;
List<RowInfo> get rowInfos {
final List<RowInfo> rows = [];
for (var block in _blocks.values) {
rows.addAll(block.rows);
}
return rows;
}
List<RowInfo> get rowInfos => _viewCache.rowInfos;
GridRowCache get rowCache => _viewCache.rowCache;
BoardDataController({required ViewPB view})
: gridId = view.id,
: viewId = view.id,
_listener = BoardListener(view.id),
// ignore: prefer_collection_literals
_blocks = LinkedHashMap(),
_gridFFIService = GridFFIService(gridId: view.id),
fieldController = GridFieldController(gridId: view.id);
_databaseFFIService = DatabaseFFIService(databaseId: view.id),
fieldController = GridFieldController(databaseId: view.id) {
//
_viewCache = DatabaseViewCache(
databaseId: view.id,
fieldController: fieldController,
);
_viewCache.addListener(onRowsChanged: (reason) {
_onRowsChanged?.call(rowInfos, reason);
});
}
void addListener({
required OnGridChanged onGridChanged,
@@ -92,8 +90,8 @@ class BoardDataController {
onDeletedGroup.call(changeset.deletedGroups);
}
if (changeset.insertedGroups.isNotEmpty) {
onInsertedGroup.call(changeset.insertedGroups);
for (final insertedGroup in changeset.insertedGroups) {
onInsertedGroup.call(insertedGroup);
}
},
(e) => _onError?.call(e),
@@ -109,56 +107,38 @@ class BoardDataController {
}
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,
);
final result = await _databaseFFIService.openGrid();
return result.fold(
(grid) async {
_onGridChanged?.call(grid);
return fieldController.loadFields(fieldIds: grid.fields).then((result) {
return result.fold(
(l) {
_loadGroups(grid.blocks);
(l) => Future(() async {
await _loadGroups();
_viewCache.rowCache.initializeRows(grid.rows);
return left(l);
},
}),
(err) => right(err),
);
},
(err) => right(err),
),
});
},
(err) => right(err),
);
}
Future<Either<RowPB, FlowyError>> createBoardCard(String groupId,
{String? startRowId}) {
return _gridFFIService.createBoardCard(groupId, startRowId);
return _databaseFFIService.createBoardCard(groupId, startRowId);
}
Future<void> dispose() async {
await _gridFFIService.closeGrid();
await _viewCache.dispose();
await _databaseFFIService.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();
Future<void> _loadGroups() async {
final result = await _databaseFFIService.loadGroups();
return Future(
() => result.fold(
(groups) {
@@ -2,13 +2,13 @@ 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:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/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';
import 'package:appflowy_backend/protobuf/flowy-database/group.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/group_changeset.pb.dart';
typedef GroupUpdateValue = Either<GroupViewChangesetPB, FlowyError>;
typedef GroupUpdateValue = Either<GroupChangesetPB, FlowyError>;
typedef GroupByNewFieldValue = Either<List<GroupPB>, FlowyError>;
class BoardListener {
@@ -16,7 +16,7 @@ class BoardListener {
PublishNotifier<GroupUpdateValue>? _groupUpdateNotifier = PublishNotifier();
PublishNotifier<GroupByNewFieldValue>? _groupByNewFieldNotifier =
PublishNotifier();
GridNotificationListener? _listener;
DatabaseNotificationListener? _listener;
BoardListener(this.viewId);
void start({
@@ -25,28 +25,28 @@ class BoardListener {
}) {
_groupUpdateNotifier?.addPublishListener(onBoardChanged);
_groupByNewFieldNotifier?.addPublishListener(onGroupByNewField);
_listener = GridNotificationListener(
_listener = DatabaseNotificationListener(
objectId: viewId,
handler: _handler,
);
}
void _handler(
GridDartNotification ty,
DatabaseNotification ty,
Either<Uint8List, FlowyError> result,
) {
switch (ty) {
case GridDartNotification.DidUpdateGroupView:
case DatabaseNotification.DidUpdateGroups:
result.fold(
(payload) => _groupUpdateNotifier?.value =
left(GroupViewChangesetPB.fromBuffer(payload)),
left(GroupChangesetPB.fromBuffer(payload)),
(error) => _groupUpdateNotifier?.value = right(error),
);
break;
case GridDartNotification.DidGroupByNewField:
case DatabaseNotification.DidGroupByField:
result.fold(
(payload) => _groupByNewFieldNotifier?.value =
left(GroupViewChangesetPB.fromBuffer(payload).newGroups),
left(GroupChangesetPB.fromBuffer(payload).initialGroups),
(error) => _groupByNewFieldNotifier?.value = right(error),
);
break;
@@ -64,7 +64,7 @@ class BoardCheckboxCellState with _$BoardCheckboxCellState {
required bool isSelected,
}) = _CheckboxCellState;
factory BoardCheckboxCellState.initial(GridCellController context) {
factory BoardCheckboxCellState.initial(GridTextCellController context) {
return BoardCheckboxCellState(
isSelected: _isSelected(context.getCellData()));
}
@@ -1,6 +1,6 @@
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/date_type_option_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@@ -60,7 +60,7 @@ class BoardNumberCellState with _$BoardNumberCellState {
required String content,
}) = _BoardNumberCellState;
factory BoardNumberCellState.initial(GridCellController context) =>
factory BoardNumberCellState.initial(GridTextCellController context) =>
BoardNumberCellState(
content: context.getCellData() ?? "",
);
@@ -1,5 +1,5 @@
import 'dart:async';
import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
@@ -1,5 +1,4 @@
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@@ -7,7 +6,7 @@ import 'dart:async';
part 'board_text_cell_bloc.freezed.dart';
class BoardTextCellBloc extends Bloc<BoardTextCellEvent, BoardTextCellState> {
final GridCellController cellController;
final GridTextCellController cellController;
void Function()? _onCellChangedFn;
BoardTextCellBloc({
required this.cellController,
@@ -72,7 +71,7 @@ class BoardTextCellState with _$BoardTextCellState {
required bool enableEdit,
}) = _BoardTextCellState;
factory BoardTextCellState.initial(GridCellController context) =>
factory BoardTextCellState.initial(GridTextCellController context) =>
BoardTextCellState(
content: context.getCellData() ?? "",
enableEdit: false,
@@ -1,5 +1,5 @@
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/url_type_option_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@@ -3,7 +3,7 @@ import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_servic
import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
import 'package:app_flowy/plugins/grid/application/row/row_service.dart';
import 'package:equatable/equatable.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@@ -18,12 +18,11 @@ class BoardCardBloc extends Bloc<BoardCardEvent, BoardCardState> {
BoardCardBloc({
required this.groupFieldId,
required String gridId,
required String viewId,
required CardDataController dataController,
required bool isEditing,
}) : _rowService = RowFFIService(
gridId: gridId,
blockId: dataController.rowPB.blockId,
databaseId: viewId,
),
_dataController = dataController,
super(
@@ -61,7 +60,7 @@ class BoardCardBloc extends Bloc<BoardCardEvent, BoardCardState> {
RowInfo rowInfo() {
return RowInfo(
gridId: _rowService.gridId,
databaseId: _rowService.databaseId,
fields: UnmodifiableListView(
state.cells.map((cell) => cell.identifier.fieldInfo).toList(),
),
@@ -3,7 +3,7 @@ import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_servic
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:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
import 'package:flutter/foundation.dart';
typedef OnCardChanged = void Function(GridCellMap, RowsChangedReason);
@@ -1,20 +1,12 @@
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
class BoardGroupService {
final String gridId;
final String viewId;
FieldPB? groupField;
BoardGroupService(this.gridId);
BoardGroupService(this.viewId);
void setGroupField(FieldPB field) {
groupField = field;
}
}
abstract class CanBeGroupField {
String get groupContent;
}
// class SingleSelectGroup extends CanBeGroupField {
// final SingleSelectTypeOptionContext typeOptionContext;
// }
@@ -1,6 +1,6 @@
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart';
import 'group_listener.dart';
typedef OnGroupError = void Function(FlowyError);
@@ -18,7 +18,7 @@ class GroupController {
final GroupControllerDelegate delegate;
GroupController({
required String gridId,
required String databaseId,
required this.group,
required this.delegate,
}) : _listener = GroupListener(group);
@@ -2,36 +2,36 @@ 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:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/notification.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/group.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/group_changeset.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/group_changeset.pb.dart';
typedef UpdateGroupNotifiedValue = Either<GroupRowsNotificationPB, FlowyError>;
class GroupListener {
final GroupPB group;
PublishNotifier<UpdateGroupNotifiedValue>? _groupNotifier = PublishNotifier();
GridNotificationListener? _listener;
DatabaseNotificationListener? _listener;
GroupListener(this.group);
void start({
required void Function(UpdateGroupNotifiedValue) onGroupChanged,
}) {
_groupNotifier?.addPublishListener(onGroupChanged);
_listener = GridNotificationListener(
_listener = DatabaseNotificationListener(
objectId: group.groupId,
handler: _handler,
);
}
void _handler(
GridDartNotification ty,
DatabaseNotification ty,
Either<Uint8List, FlowyError> result,
) {
switch (ty) {
case GridDartNotification.DidUpdateGroup:
case DatabaseNotification.DidUpdateGroupRow:
result.fold(
(payload) => _groupNotifier?.value =
left(GroupRowsNotificationPB.fromBuffer(payload)),
@@ -6,8 +6,8 @@ import 'package:dartz/dartz.dart';
part 'board_setting_bloc.freezed.dart';
class BoardSettingBloc extends Bloc<BoardSettingEvent, BoardSettingState> {
final String gridId;
BoardSettingBloc({required this.gridId})
final String databaseId;
BoardSettingBloc({required this.databaseId})
: super(BoardSettingState.initial()) {
on<BoardSettingEvent>(
(event, emit) async {
@@ -1,8 +1,10 @@
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/plugins/util.dart';
import 'package:app_flowy/startup/plugin/plugin.dart';
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
import 'package:app_flowy/workspace/presentation/widgets/left_bar_item.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:app_flowy/startup/plugin/plugin.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'presentation/board_page.dart';
@@ -18,7 +20,7 @@ class BoardPluginBuilder implements PluginBuilder {
}
@override
String get menuName => "Board";
String get menuName => LocaleKeys.board_menuName.tr();
@override
String get menuIcon => "editor/board";
@@ -4,35 +4,40 @@ import 'dart:collection';
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/plugins/board/application/card/card_data_controller.dart';
import 'package:app_flowy/plugins/grid/application/row/row_cache.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:app_flowy/plugins/grid/application/row/row_data_controller.dart';
import 'package:app_flowy/plugins/grid/presentation/widgets/cell/cell_builder.dart';
import 'package:app_flowy/plugins/grid/presentation/widgets/row/row_detail.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';
import 'package:appflowy_board/appflowy_board.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/flowy_infra_ui_web.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/error_page.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:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../grid/application/row/row_cache.dart';
import '../application/board_bloc.dart';
import 'card/card.dart';
import 'card/card_cell_builder.dart';
import 'toolbar/board_toolbar.dart';
class BoardPage extends StatelessWidget {
final ViewPB view;
BoardPage({
required this.view,
Key? key,
this.onEditStateChanged,
}) : super(key: ValueKey(view.id));
final ViewPB view;
/// Called when edit state changed
final VoidCallback? onEditStateChanged;
@override
Widget build(BuildContext context) {
return BlocProvider(
@@ -46,7 +51,9 @@ class BoardPage extends StatelessWidget {
const Center(child: CircularProgressIndicator.adaptive()),
finish: (result) {
return result.successOrFail.fold(
(_) => const BoardContent(),
(_) => BoardContent(
onEditStateChanged: onEditStateChanged,
),
(err) => FlowyErrorPage(err.toString()),
);
},
@@ -58,7 +65,12 @@ class BoardPage extends StatelessWidget {
}
class BoardContent extends StatefulWidget {
const BoardContent({Key? key}) : super(key: key);
const BoardContent({
Key? key,
this.onEditStateChanged,
}) : super(key: key);
final VoidCallback? onEditStateChanged;
@override
State<BoardContent> createState() => _BoardContentState();
@@ -80,7 +92,10 @@ class _BoardContentState extends State<BoardContent> {
@override
Widget build(BuildContext context) {
return BlocListener<BoardBloc, BoardState>(
listener: (context, state) => _handleEditStateChanged(state, context),
listener: (context, state) {
_handleEditStateChanged(state, context);
widget.onEditStateChanged?.call();
},
child: BlocBuilder<BoardBloc, BoardState>(
buildWhen: (previous, current) => previous.groupIds != current.groupIds,
builder: (context, state) {
@@ -212,7 +227,7 @@ class _BoardContentState extends State<BoardContent> {
if (rowCache == null) return SizedBox(key: ObjectKey(groupItem));
final fieldController = context.read<BoardBloc>().fieldController;
final gridId = context.read<BoardBloc>().gridId;
final databaseId = context.read<BoardBloc>().databaseId;
final cardController = CardDataController(
fieldController: fieldController,
rowCache: rowCache,
@@ -234,14 +249,14 @@ class _BoardContentState extends State<BoardContent> {
margin: config.cardPadding,
decoration: _makeBoxDecoration(context),
child: BoardCard(
gridId: gridId,
viewId: databaseId,
groupId: groupData.group.groupId,
fieldId: groupItem.fieldInfo.id,
isEditing: isEditing,
cellBuilder: cellBuilder,
dataController: cardController,
openCard: (context) => _openCard(
gridId,
databaseId,
fieldController,
rowPB,
rowCache,
@@ -277,19 +292,19 @@ class _BoardContentState extends State<BoardContent> {
}
void _openCard(
String gridId,
String databaseId,
GridFieldController fieldController,
RowPB rowPB,
GridRowCache rowCache,
BuildContext context,
) {
final rowInfo = RowInfo(
gridId: gridId,
databaseId: databaseId,
fields: UnmodifiableListView(fieldController.fieldInfos),
rowPB: rowPB,
);
final dataController = GridRowDataController(
final dataController = RowDataController(
rowInfo: rowInfo,
fieldController: fieldController,
rowCache: rowCache,
@@ -316,7 +331,7 @@ class _ToolbarBlocAdaptor extends StatelessWidget {
builder: (context, state) {
final bloc = context.read<BoardBloc>();
final toolbarContext = BoardToolbarContext(
viewId: bloc.gridId,
viewId: bloc.databaseId,
fieldController: bloc.fieldController,
);
@@ -358,6 +373,8 @@ Widget? _buildHeaderIcon(GroupData customData) {
break;
case FieldType.URL:
break;
case FieldType.Checklist:
break;
}
if (widget != null) {
@@ -0,0 +1,38 @@
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
import 'package:app_flowy/plugins/grid/application/cell/checklist_cell_bloc.dart';
import 'package:app_flowy/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_progress_bar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class BoardChecklistCell extends StatefulWidget {
final GridCellControllerBuilder cellControllerBuilder;
const BoardChecklistCell({required this.cellControllerBuilder, Key? key})
: super(key: key);
@override
State<BoardChecklistCell> createState() => _BoardChecklistCellState();
}
class _BoardChecklistCellState extends State<BoardChecklistCell> {
late ChecklistCellBloc _cellBloc;
@override
void initState() {
final cellController =
widget.cellControllerBuilder.build() as GridChecklistCellController;
_cellBloc = ChecklistCellBloc(cellController: cellController);
_cellBloc.add(const ChecklistCellEvent.initial());
super.initState();
}
@override
Widget build(BuildContext context) {
return BlocProvider.value(
value: _cellBloc,
child: BlocBuilder<ChecklistCellBloc, ChecklistCellState>(
builder: (context, state) =>
ChecklistProgressBar(percent: state.percent),
),
);
}
}
@@ -35,7 +35,7 @@ class _BoardTextCellState extends State<BoardTextCell> {
@override
void initState() {
final cellController =
widget.cellControllerBuilder.build() as GridCellController;
widget.cellControllerBuilder.build() as GridTextCellController;
_cellBloc = BoardTextCellBloc(cellController: cellController)
..add(const BoardTextCellEvent.initial());
_controller = TextEditingController(text: _cellBloc.state.content);
@@ -13,7 +13,7 @@ import 'container/accessory.dart';
import 'container/card_container.dart';
class BoardCard extends StatefulWidget {
final String gridId;
final String viewId;
final String groupId;
final String fieldId;
final bool isEditing;
@@ -24,7 +24,7 @@ class BoardCard extends StatefulWidget {
final VoidCallback onEndEditing;
const BoardCard({
required this.gridId,
required this.viewId,
required this.groupId,
required this.fieldId,
required this.isEditing,
@@ -50,7 +50,7 @@ class _BoardCardState extends State<BoardCard> {
void initState() {
rowNotifier = EditableRowNotifier(isEditing: widget.isEditing);
_cardBloc = BoardCardBloc(
gridId: widget.gridId,
viewId: widget.viewId,
groupFieldId: widget.fieldId,
dataController: widget.dataController,
isEditing: widget.isEditing,
@@ -1,9 +1,10 @@
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:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart';
import 'package:flutter/material.dart';
import 'board_cell.dart';
import 'board_checkbox_cell.dart';
import 'board_checklist_cell.dart';
import 'board_date_cell.dart';
import 'board_number_cell.dart';
import 'board_select_option_cell.dart';
@@ -58,6 +59,11 @@ class BoardCellBuilder {
editableNotifier: cellNotifier,
key: key,
);
case FieldType.Checklist:
return BoardChecklistCell(
cellControllerBuilder: cellControllerBuilder,
key: key,
);
case FieldType.Number:
return BoardNumberCell(
groupId: groupId,
@@ -13,6 +13,7 @@ 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 'board_toolbar.dart';
@@ -43,7 +44,7 @@ class BoardSettingList extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => BoardSettingBloc(gridId: settingContext.viewId),
create: (context) => BoardSettingBloc(databaseId: settingContext.viewId),
child: BlocListener<BoardSettingBloc, BoardSettingState>(
listenWhen: (previous, current) =>
previous.selectedAction != current.selectedAction,
@@ -170,7 +171,7 @@ class _BoardSettingListPopoverState extends State<BoardSettingListPopover> {
);
case BoardSettingAction.properties:
return GridPropertyList(
gridId: widget.settingContext.viewId,
databaseId: widget.settingContext.viewId,
fieldController: widget.settingContext.fieldController,
);
}
@@ -181,6 +182,6 @@ class _BoardSettingListPopoverState extends State<BoardSettingListPopover> {
onAction: (action, settingContext) {
setState(() => _action = action);
},
);
).padding(all: 6.0);
}
}
@@ -1,7 +1,8 @@
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/color_extension.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flutter/material.dart';
@@ -67,12 +68,12 @@ class _SettingButtonState extends State<_SettingButton> {
direction: PopoverDirection.leftWithTopAligned,
triggerActions: PopoverTriggerFlags.none,
constraints: BoxConstraints.loose(const Size(260, 400)),
margin: EdgeInsets.zero,
child: FlowyTextButton(
LocaleKeys.settings_title.tr(),
fontSize: 14,
fillColor: Colors.transparent,
hoverColor: AFThemeExtension.of(context).lightGreyHover,
padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 6),
padding: GridSize.typeOptionContentInsets,
onPressed: () {
popoverController.show();
},
@@ -0,0 +1,89 @@
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/startup/plugin/plugin.dart';
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
import 'package:app_flowy/workspace/presentation/widgets/left_bar_item.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:flutter/material.dart';
import '../util.dart';
import 'presentation/calendar_page.dart';
class CalendarPluginBuilder extends PluginBuilder {
@override
Plugin build(dynamic data) {
if (data is ViewPB) {
return CalendarPlugin(pluginType: pluginType, view: data);
} else {
throw FlowyPluginException.invalidData;
}
}
@override
String get menuName => LocaleKeys.calendar_menuName.tr();
@override
String get menuIcon => "editor/date";
@override
PluginType get pluginType => PluginType.calendar;
@override
ViewDataFormatPB get dataFormatType => ViewDataFormatPB.DatabaseFormat;
@override
ViewLayoutTypePB? get layoutType => ViewLayoutTypePB.Calendar;
}
class CalendarPluginConfig implements PluginConfig {
@override
bool get creatable => false;
}
class CalendarPlugin extends Plugin {
@override
final ViewPluginNotifier notifier;
final PluginType _pluginType;
CalendarPlugin({
required ViewPB view,
required PluginType pluginType,
}) : _pluginType = pluginType,
notifier = ViewPluginNotifier(view: view);
@override
PluginDisplay get display => CalendarPluginDisplay(notifier: notifier);
@override
PluginId get id => notifier.view.id;
@override
PluginType get ty => _pluginType;
}
class CalendarPluginDisplay extends PluginDisplay {
final ViewPluginNotifier notifier;
CalendarPluginDisplay({required this.notifier, Key? key});
ViewPB get view => notifier.view;
@override
Widget get leftBarItem => ViewLeftBarItem(view: view);
@override
Widget buildWidget(PluginContext context) {
notifier.isDeleted.addListener(() {
notifier.isDeleted.value.fold(() => null, (deletedView) {
if (deletedView.hasIndex()) {
context.onDeleted(view, deletedView.index);
}
});
});
return CalendarPage(key: ValueKey(view.id));
// return CalendarPage(key: ValueKey(view.id), view: view);
}
@override
List<NavigationItem> get navigationItems => [this];
}
@@ -0,0 +1,162 @@
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart';
import 'package:calendar_view/calendar_view.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra/theme_extension.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:flutter/material.dart';
import 'package:styled_widget/styled_widget.dart';
import 'layout/sizes.dart';
import 'toolbar/calendar_toolbar.dart';
class CalendarPage extends StatelessWidget {
const CalendarPage({super.key});
@override
Widget build(BuildContext context) {
return const CalendarContent();
}
}
class CalendarContent extends StatefulWidget {
const CalendarContent({super.key});
@override
State<CalendarContent> createState() => _CalendarContentState();
}
class _CalendarContentState extends State<CalendarContent> {
late EventController _eventController;
GlobalKey<MonthViewState>? _calendarState;
@override
void initState() {
_eventController = EventController();
_calendarState = GlobalKey<MonthViewState>();
super.initState();
}
@override
Widget build(BuildContext context) {
return CalendarControllerProvider(
controller: _eventController,
child: Column(
children: [
// const _ToolbarBlocAdaptor(),
_toolbar(),
_buildCalendar(_eventController),
],
),
);
}
Widget _toolbar() {
return const CalendarToolbar();
}
Widget _buildCalendar(EventController eventController) {
return Expanded(
child: MonthView(
key: _calendarState,
controller: _eventController,
cellAspectRatio: 1.75,
borderColor: Theme.of(context).dividerColor,
headerBuilder: _headerNavigatorBuilder,
weekDayBuilder: _headerWeekDayBuilder,
cellBuilder: _calendarDayBuilder,
),
);
}
Widget _headerNavigatorBuilder(DateTime currentMonth) {
return Row(
children: [
FlowyText.medium(
DateFormat('MMMM y', context.locale.toLanguageTag())
.format(currentMonth),
),
const Spacer(),
FlowyIconButton(
width: CalendarSize.navigatorButtonWidth,
height: CalendarSize.navigatorButtonHeight,
icon: svgWidget('home/arrow_left'),
tooltipText: LocaleKeys.calendar_navigation_previousMonth.tr(),
hoverColor: AFThemeExtension.of(context).lightGreyHover,
onPressed: () => _calendarState?.currentState?.previousPage(),
),
FlowyTextButton(
LocaleKeys.calendar_navigation_today.tr(),
fillColor: Colors.transparent,
fontWeight: FontWeight.w500,
tooltip: LocaleKeys.calendar_navigation_jumpToday.tr(),
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
hoverColor: AFThemeExtension.of(context).lightGreyHover,
onPressed: () =>
_calendarState?.currentState?.animateToMonth(DateTime.now()),
),
FlowyIconButton(
width: CalendarSize.navigatorButtonWidth,
height: CalendarSize.navigatorButtonHeight,
icon: svgWidget('home/arrow_right'),
tooltipText: LocaleKeys.calendar_navigation_nextMonth.tr(),
hoverColor: AFThemeExtension.of(context).lightGreyHover,
onPressed: () => _calendarState?.currentState?.nextPage(),
),
],
);
}
Widget _headerWeekDayBuilder(day) {
final symbols = DateFormat.EEEE(context.locale.toLanguageTag()).dateSymbols;
final weekDayString = symbols.WEEKDAYS[day];
return Center(
child: Padding(
padding: CalendarSize.daysOfWeekInsets,
child: FlowyText.medium(
weekDayString,
color: Theme.of(context).hintColor,
),
),
);
}
Widget _calendarDayBuilder(date, event, isToday, isInMonth) {
Color dayTextColor = Theme.of(context).colorScheme.onSurface;
Color cellBackgroundColor = Theme.of(context).colorScheme.surface;
String dayString = date.day == 1
? DateFormat('MMM d', context.locale.toLanguageTag()).format(date)
: date.day.toString();
if (isToday) {
dayTextColor = Theme.of(context).colorScheme.onPrimary;
}
if (!isInMonth) {
dayTextColor = Theme.of(context).disabledColor;
cellBackgroundColor = AFThemeExtension.of(context).lightGreyHover;
}
Widget day = Container(
decoration: BoxDecoration(
color: isToday ? Theme.of(context).colorScheme.primary : null,
borderRadius: Corners.s6Border,
),
padding: GridSize.typeOptionContentInsets,
child: FlowyText.medium(
dayString,
color: dayTextColor,
),
);
return Container(
color: cellBackgroundColor,
child: Align(
alignment: Alignment.topRight,
child: day.padding(all: 6.0),
),
);
}
}
@@ -0,0 +1,11 @@
import 'package:flutter/widgets.dart';
class CalendarSize {
static double scale = 1;
static double get scrollBarSize => 12 * scale;
static double get navigatorButtonWidth => 20 * scale;
static double get navigatorButtonHeight => 25 * scale;
static EdgeInsets get daysOfWeekInsets =>
EdgeInsets.symmetric(vertical: 10.0 * scale);
}
@@ -0,0 +1,23 @@
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flutter/material.dart';
class CalendarToolbar extends StatelessWidget {
const CalendarToolbar({super.key});
@override
Widget build(BuildContext context) {
return SizedBox(
height: 40,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: const [
FlowyTextButton(
"Settings",
fillColor: Colors.transparent,
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
),
],
),
);
}
}
@@ -4,10 +4,10 @@ import 'package:app_flowy/workspace/application/view/view_listener.dart';
import 'package:app_flowy/plugins/document/application/doc_service.dart';
import 'package:appflowy_editor/appflowy_editor.dart'
show EditorState, Document, Transaction;
import 'package:flowy_sdk/protobuf/flowy-folder/trash.pb.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/log.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/trash.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_backend/log.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:dartz/dartz.dart';
@@ -1,9 +1,9 @@
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-document/entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart';
class DocumentService {
Future<Either<DocumentSnapshotPB, FlowyError>> openDocument({
@@ -1,9 +1,11 @@
import 'dart:convert';
import 'dart:io';
import 'package:app_flowy/plugins/document/application/share_service.dart';
import 'package:flowy_sdk/protobuf/flowy-document/entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/parsers/divider_node_parser.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/parsers/math_equation_node_parser.dart';
import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:dartz/dartz.dart';
@@ -48,7 +50,10 @@ class DocShareBloc extends Bloc<DocShareEvent, DocShareState> {
String _convertDocumentToMarkdown(ExportDataPB value) {
final json = jsonDecode(value.data);
final document = Document.fromJson(json);
return documentToMarkdown(document);
return documentToMarkdown(document, customParsers: [
const DividerNodeParser(),
const MathEquationNodeParser(),
]);
}
}
@@ -1,9 +1,9 @@
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-document/protobuf.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-document/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
class ShareService {
Future<Either<ExportDataPB, FlowyError>> export(
@@ -1,29 +1,19 @@
library document_plugin;
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/plugins/document/document_page.dart';
import 'package:app_flowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:app_flowy/plugins/document/presentation/more/more_button.dart';
import 'package:app_flowy/plugins/document/presentation/share/share_button.dart';
import 'package:app_flowy/plugins/util.dart';
import 'package:app_flowy/startup/plugin/plugin.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/plugins/document/application/share_bloc.dart';
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
import 'package:app_flowy/workspace/presentation/home/toast.dart';
import 'package:app_flowy/workspace/presentation/widgets/left_bar_item.dart';
import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:clipboard/clipboard.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flowy_infra_ui/widget/rounded_button.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-document/entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'document_page.dart';
class DocumentPluginBuilder extends PluginBuilder {
@override
Plugin build(dynamic data) {
@@ -44,11 +34,13 @@ class DocumentPluginBuilder extends PluginBuilder {
PluginType get pluginType => PluginType.editor;
@override
ViewDataFormatPB get dataFormatType => ViewDataFormatPB.TreeFormat;
ViewDataFormatPB get dataFormatType => ViewDataFormatPB.NodeFormat;
}
class DocumentPlugin extends Plugin<int> {
late PluginType _pluginType;
final DocumentAppearanceCubit _documentAppearanceCubit =
DocumentAppearanceCubit();
@override
final ViewPluginNotifier notifier;
@@ -59,10 +51,22 @@ class DocumentPlugin extends Plugin<int> {
Key? key,
}) : notifier = ViewPluginNotifier(view: view) {
_pluginType = pluginType;
_documentAppearanceCubit.fetch();
}
@override
PluginDisplay get display => DocumentPluginDisplay(notifier: notifier);
void dispose() {
_documentAppearanceCubit.close();
super.dispose();
}
@override
PluginDisplay get display {
return DocumentPluginDisplay(
notifier: notifier,
documentAppearanceCubit: _documentAppearanceCubit,
);
}
@override
PluginType get ty => _pluginType;
@@ -75,8 +79,13 @@ class DocumentPluginDisplay extends PluginDisplay with NavigationItem {
final ViewPluginNotifier notifier;
ViewPB get view => notifier.view;
int? deletedViewIndex;
DocumentAppearanceCubit documentAppearanceCubit;
DocumentPluginDisplay({required this.notifier, Key? key});
DocumentPluginDisplay({
required this.notifier,
required this.documentAppearanceCubit,
Key? key,
});
@override
Widget buildWidget(PluginContext context) {
@@ -88,10 +97,17 @@ class DocumentPluginDisplay extends PluginDisplay with NavigationItem {
});
});
return DocumentPage(
view: view,
onDeleted: () => context.onDeleted(view, deletedViewIndex),
key: ValueKey(view.id),
return BlocProvider.value(
value: documentAppearanceCubit,
child: BlocBuilder<DocumentAppearanceCubit, DocumentAppearance>(
builder: (_, state) {
return DocumentPage(
view: view,
onDeleted: () => context.onDeleted(view, deletedViewIndex),
key: ValueKey(view.id),
);
},
),
);
}
@@ -99,133 +115,19 @@ class DocumentPluginDisplay extends PluginDisplay with NavigationItem {
Widget get leftBarItem => ViewLeftBarItem(view: view);
@override
Widget? get rightBarItem => DocumentShareButton(view: view);
Widget? get rightBarItem {
return Row(
children: [
DocumentShareButton(view: view),
const SizedBox(width: 10),
BlocProvider.value(
value: documentAppearanceCubit,
child: const DocumentMoreButton(),
),
],
);
}
@override
List<NavigationItem> get navigationItems => [this];
}
class DocumentShareButton extends StatelessWidget {
final ViewPB view;
DocumentShareButton({Key? key, required this.view})
: super(key: ValueKey(view.hashCode));
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => getIt<DocShareBloc>(param1: view),
child: BlocListener<DocShareBloc, DocShareState>(
listener: (context, state) {
state.map(
initial: (_) {},
loading: (_) {},
finish: (state) {
state.successOrFail.fold(
_handleExportData,
_handleExportError,
);
},
);
},
child: BlocBuilder<DocShareBloc, DocShareState>(
builder: (context, state) => ConstrainedBox(
constraints: const BoxConstraints.expand(
height: 30,
width: 100,
),
child: ShareActionList(view: view),
),
),
),
);
}
void _handleExportData(ExportDataPB exportData) {
switch (exportData.exportType) {
case ExportType.Link:
break;
case ExportType.Markdown:
FlutterClipboard.copy(exportData.data)
.then((value) => Log.info('copied to clipboard'));
break;
case ExportType.Text:
break;
}
}
void _handleExportError(FlowyError error) {}
}
class ShareActionList extends StatelessWidget {
const ShareActionList({
Key? key,
required this.view,
}) : super(key: key);
final ViewPB view;
@override
Widget build(BuildContext context) {
final docShareBloc = context.read<DocShareBloc>();
return PopoverActionList<ShareActionWrapper>(
direction: PopoverDirection.bottomWithCenterAligned,
actions: ShareAction.values
.map((action) => ShareActionWrapper(action))
.toList(),
buildChild: (controller) {
return RoundedTextButton(
title: LocaleKeys.shareAction_buttonText.tr(),
onPressed: () => controller.show(),
);
},
onSelected: (action, controller) async {
switch (action.inner) {
case ShareAction.markdown:
final exportPath = await FilePicker.platform.saveFile(
dialogTitle: '',
fileName: '${view.name}.md',
);
if (exportPath != null) {
docShareBloc.add(DocShareEvent.shareMarkdown(exportPath));
showMessageToast('Exported to: $exportPath');
}
break;
case ShareAction.copyLink:
NavigatorAlertDialog(
title: LocaleKeys.shareAction_workInProgress.tr())
.show(context);
break;
}
controller.close();
},
);
}
}
enum ShareAction {
markdown,
copyLink,
}
class ShareActionWrapper extends ActionCell {
final ShareAction inner;
ShareActionWrapper(this.inner);
@override
Widget? leftIcon(Color iconColor) => null;
@override
String get name => inner.name;
}
extension QuestionBubbleExtension on ShareAction {
String get name {
switch (this) {
case ShareAction.markdown:
return LocaleKeys.shareAction_markdown.tr();
case ShareAction.copyLink:
return LocaleKeys.shareAction_copyLink.tr();
}
}
}
@@ -1,14 +1,19 @@
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:app_flowy/plugins/document/presentation/plugins/board/board_menu_item.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/board/board_node_widget.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/grid/grid_menu_item.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/grid/grid_node_widget.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.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 '../../startup/startup.dart';
import 'application/doc_bloc.dart';
import 'editor_styles.dart';
import 'presentation/banner.dart';
class DocumentPage extends StatefulWidget {
final VoidCallback onDeleted;
@@ -96,15 +101,46 @@ class _DocumentPageState extends State<DocumentPage> {
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(),
// Divider
kDividerType: DividerWidgetBuilder(),
// Math Equation
kMathEquationType: MathEquationNodeWidgetBuidler(),
// Code Block
kCodeBlockType: CodeBlockNodeWidgetBuilder(),
// Board
kBoardType: BoardNodeWidgetBuilder(),
// Grid
kGridType: GridNodeWidgetBuilder(),
// Card
kCalloutType: CalloutNodeWidgetBuilder(),
},
shortcutEvents: [
insertHorizontalRule,
// Divider
insertDividerEvent,
// Code Block
enterInCodeBlock,
ignoreKeysInCodeBlock,
pasteInCodeBlock,
],
selectionMenuItems: [
// Divider
dividerMenuItem,
// Math Equation
mathEquationMenuItem,
// Code Block
codeBlockMenuItem,
// Emoji
emojiMenuItem,
// Board
boardMenuItem,
// Grid
gridMenuItem,
// Callout
calloutMenuItem,
],
themeData: theme.copyWith(extensions: [
...theme.extensions.values,
@@ -115,8 +151,8 @@ class _DocumentPageState extends State<DocumentPage> {
return Expanded(
child: Center(
child: Container(
constraints: BoxConstraints(
maxWidth: editorMaxWidth,
constraints: const BoxConstraints(
maxWidth: double.infinity,
),
child: editor,
),
@@ -1,31 +1,39 @@
import 'package:app_flowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
const _baseFontSize = 14.0;
import 'package:provider/provider.dart';
EditorStyle customEditorTheme(BuildContext context) {
final documentStyle = context.watch<DocumentAppearanceCubit>().state;
var editorStyle = Theme.of(context).brightness == Brightness.dark
? EditorStyle.dark
: EditorStyle.light;
editorStyle = editorStyle.copyWith(
padding: const EdgeInsets.all(0),
padding: const EdgeInsets.symmetric(horizontal: 100, vertical: 28),
textStyle: editorStyle.textStyle?.copyWith(
fontFamily: 'poppins',
fontSize: _baseFontSize,
fontSize: documentStyle.fontSize,
),
placeholderTextStyle: editorStyle.placeholderTextStyle?.copyWith(
fontFamily: 'poppins',
fontSize: _baseFontSize,
fontSize: documentStyle.fontSize,
),
bold: editorStyle.bold?.copyWith(
fontWeight: FontWeight.w500,
fontWeight: FontWeight.w600,
fontFamily: 'poppins-Bold',
),
backgroundColor: Theme.of(context).colorScheme.surface,
selectionMenuItemSelectedIconColor:
Theme.of(context).textTheme.bodyMedium?.color,
selectionMenuItemSelectedTextColor:
Theme.of(context).textTheme.bodyMedium?.color,
);
return editorStyle;
}
Iterable<ThemeExtension<dynamic>> customPluginTheme(BuildContext context) {
final documentStyle = context.watch<DocumentAppearanceCubit>().state;
final baseFontSize = documentStyle.fontSize;
const basePadding = 12.0;
var headingPluginStyle = Theme.of(context).brightness == Brightness.dark
? HeadingPluginStyle.dark
@@ -33,15 +41,15 @@ Iterable<ThemeExtension<dynamic>> customPluginTheme(BuildContext context) {
headingPluginStyle = headingPluginStyle.copyWith(
textStyle: (EditorState editorState, Node node) {
final headingToFontSize = {
'h1': _baseFontSize + 12,
'h2': _baseFontSize + 8,
'h3': _baseFontSize + 4,
'h4': _baseFontSize,
'h5': _baseFontSize,
'h6': _baseFontSize,
'h1': baseFontSize + 12,
'h2': baseFontSize + 8,
'h3': baseFontSize + 4,
'h4': baseFontSize,
'h5': baseFontSize,
'h6': baseFontSize,
};
final fontSize =
headingToFontSize[node.attributes.heading] ?? _baseFontSize;
headingToFontSize[node.attributes.heading] ?? baseFontSize;
return TextStyle(fontSize: fontSize, fontWeight: FontWeight.w600);
},
padding: (EditorState editorState, Node node) {
@@ -57,10 +65,28 @@ Iterable<ThemeExtension<dynamic>> customPluginTheme(BuildContext context) {
return EdgeInsets.only(bottom: padding);
},
);
var numberListPluginStyle = Theme.of(context).brightness == Brightness.dark
? NumberListPluginStyle.dark
: NumberListPluginStyle.light;
numberListPluginStyle = numberListPluginStyle.copyWith(
icon: (_, textNode) {
const iconPadding = EdgeInsets.only(left: 5.0, right: 5.0);
return Container(
padding: iconPadding,
child: Text(
'${textNode.attributes.number.toString()}.',
style: customEditorTheme(context).textStyle,
),
);
},
);
final pluginTheme = Theme.of(context).brightness == Brightness.dark
? darkPlguinStyleExtension
: lightPlguinStyleExtension;
return pluginTheme.toList()
..removeWhere((element) => element is HeadingPluginStyle)
..add(headingPluginStyle);
..removeWhere((element) =>
element is HeadingPluginStyle || element is NumberListPluginStyle)
..add(headingPluginStyle)
..add(numberListPluginStyle);
}
@@ -0,0 +1,40 @@
import 'package:bloc/bloc.dart';
import 'package:shared_preferences/shared_preferences.dart';
const String _kDocumentAppearenceFontSize = 'kDocumentAppearenceFontSize';
class DocumentAppearance {
const DocumentAppearance({
required this.fontSize,
});
final double fontSize;
// Will be supported...
// final String fontName;
DocumentAppearance copyWith({double? fontSize}) {
return DocumentAppearance(
fontSize: fontSize ?? this.fontSize,
);
}
}
class DocumentAppearanceCubit extends Cubit<DocumentAppearance> {
DocumentAppearanceCubit() : super(const DocumentAppearance(fontSize: 14.0));
void fetch() async {
final prefs = await SharedPreferences.getInstance();
final fontSize = prefs.getDouble(_kDocumentAppearenceFontSize) ?? 14.0;
emit(state.copyWith(
fontSize: fontSize,
));
}
void syncFontSize(double fontSize) async {
final prefs = await SharedPreferences.getInstance();
prefs.setDouble(_kDocumentAppearenceFontSize, fontSize);
emit(state.copyWith(
fontSize: fontSize,
));
}
}
@@ -0,0 +1,69 @@
import 'package:app_flowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flutter/material.dart';
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:tuple/tuple.dart';
import 'package:easy_localization/easy_localization.dart';
class FontSizeSwitcher extends StatefulWidget {
const FontSizeSwitcher({
super.key,
});
@override
State<FontSizeSwitcher> createState() => _FontSizeSwitcherState();
}
class _FontSizeSwitcherState extends State<FontSizeSwitcher> {
final List<Tuple3<String, double, bool>> _fontSizes = [
Tuple3(LocaleKeys.moreAction_small.tr(), 12.0, false),
Tuple3(LocaleKeys.moreAction_medium.tr(), 14.0, true),
Tuple3(LocaleKeys.moreAction_large.tr(), 18.0, false),
];
@override
Widget build(BuildContext context) {
return BlocBuilder<DocumentAppearanceCubit, DocumentAppearance>(
builder: (context, state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
FlowyText.semibold(
LocaleKeys.moreAction_fontSize.tr(),
fontSize: 12,
),
const SizedBox(
height: 5,
),
ToggleButtons(
isSelected:
_fontSizes.map((e) => e.item2 == state.fontSize).toList(),
onPressed: (int index) {
_updateSelectedFontSize(_fontSizes[index].item2);
},
borderRadius: const BorderRadius.all(Radius.circular(5)),
selectedBorderColor: Theme.of(context).colorScheme.primaryContainer,
selectedColor: Theme.of(context).colorScheme.onSurface,
fillColor: Theme.of(context).colorScheme.primaryContainer,
color: Theme.of(context).hintColor,
constraints: const BoxConstraints(
minHeight: 40.0,
minWidth: 80.0,
),
children: _fontSizes
.map((e) => Text(
e.item1,
style: TextStyle(fontSize: e.item2),
))
.toList(),
),
],
);
});
}
void _updateSelectedFontSize(double fontSize) {
context.read<DocumentAppearanceCubit>().syncFontSize(fontSize);
}
}
@@ -0,0 +1,35 @@
import 'package:app_flowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:app_flowy/plugins/document/presentation/more/font_size_switcher.dart';
import 'package:flowy_infra/image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DocumentMoreButton extends StatelessWidget {
const DocumentMoreButton({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return PopupMenuButton<int>(
offset: const Offset(0, 30),
itemBuilder: (context) {
return [
PopupMenuItem(
value: 1,
enabled: false,
child: BlocProvider.value(
value: context.read<DocumentAppearanceCubit>(),
child: const FontSizeSwitcher(),
),
),
];
},
child: svgWidget(
'editor/details',
size: const Size(18, 18),
color: Theme.of(context).colorScheme.onSurface,
),
);
}
}
@@ -0,0 +1,187 @@
import 'package:app_flowy/plugins/document/presentation/plugins/base/insert_page_command.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/app/app_service.dart';
import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pbserver.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:dartz/dartz.dart' as dartz;
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';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
import 'package:app_flowy/workspace/application/view/view_ext.dart';
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
class BuiltInPageWidget extends StatefulWidget {
const BuiltInPageWidget({
Key? key,
required this.node,
required this.editorState,
required this.builder,
}) : super(key: key);
final Node node;
final EditorState editorState;
final Widget Function(ViewPB viewPB) builder;
@override
State<BuiltInPageWidget> createState() => _BuiltInPageWidgetState();
}
class _BuiltInPageWidgetState extends State<BuiltInPageWidget> {
final focusNode = FocusNode();
String get gridID {
return widget.node.attributes[kViewID];
}
String get appID {
return widget.node.attributes[kAppID];
}
@override
Widget build(BuildContext context) {
return FutureBuilder<dartz.Either<ViewPB, FlowyError>>(
builder: (context, snapshot) {
if (snapshot.hasData) {
final board = snapshot.data?.getLeftOrNull<ViewPB>();
if (board != null) {
return _build(context, board);
}
}
return const Center(
child: CircularProgressIndicator(),
);
},
future: AppService().getView(appID, gridID),
);
}
@override
void dispose() {
focusNode.dispose();
super.dispose();
}
Widget _build(BuildContext context, ViewPB viewPB) {
return MouseRegion(
onEnter: (event) {
widget.editorState.service.scrollService?.disable();
},
onExit: (event) {
widget.editorState.service.scrollService?.enable();
},
child: SizedBox(
height: 400,
child: Stack(
children: [
_buildMenu(context, viewPB),
_buildGrid(context, viewPB),
],
),
),
);
}
Widget _buildGrid(BuildContext context, ViewPB viewPB) {
return Focus(
focusNode: focusNode,
onFocusChange: (value) {
if (value) {
widget.editorState.service.selectionService.clearSelection();
}
},
child: widget.builder(viewPB),
);
}
Widget _buildMenu(BuildContext context, ViewPB viewPB) {
return Positioned(
top: 5,
left: 5,
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
// information
FlowyIconButton(
tooltipText: LocaleKeys.tooltip_referencePage.tr(namedArgs: {
'name': viewPB.layout.name,
}),
width: 24,
height: 24,
iconPadding: const EdgeInsets.all(3),
icon: svgWidget('common/information'),
),
// Name
const Space(7, 0),
FlowyText.medium(
viewPB.name,
fontSize: 16.0,
),
// setting
const Space(7, 0),
PopoverActionList<_ActionWrapper>(
direction: PopoverDirection.bottomWithCenterAligned,
actions: _ActionType.values
.map((action) => _ActionWrapper(action))
.toList(),
buildChild: (controller) {
return FlowyIconButton(
tooltipText: LocaleKeys.tooltip_openMenu.tr(),
width: 24,
height: 24,
iconPadding: const EdgeInsets.all(3),
icon: svgWidget('common/settings'),
onPressed: () => controller.show(),
);
},
onSelected: (action, controller) async {
switch (action.inner) {
case _ActionType.viewDatabase:
getIt<MenuSharedState>().latestOpenView = viewPB;
getIt<HomeStackManager>().setPlugin(viewPB.plugin());
break;
case _ActionType.delete:
final transaction = widget.editorState.transaction;
transaction.deleteNode(widget.node);
widget.editorState.apply(transaction);
break;
}
controller.close();
},
)
],
),
);
}
}
enum _ActionType {
viewDatabase,
delete,
}
class _ActionWrapper extends ActionCell {
final _ActionType inner;
_ActionWrapper(this.inner);
Widget? icon(Color iconColor) => null;
@override
String get name {
switch (inner) {
case _ActionType.viewDatabase:
return LocaleKeys.tooltip_viewDataBase.tr();
case _ActionType.delete:
return LocaleKeys.disclosureAction_delete.tr();
}
}
}
@@ -0,0 +1,42 @@
import 'package:app_flowy/plugins/document/presentation/plugins/board/board_node_widget.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/grid/grid_node_widget.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/app.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
const String kAppID = 'app_id';
const String kViewID = 'view_id';
extension InsertPage on EditorState {
void insertPage(AppPB appPB, ViewPB viewPB) {
final selection = service.selectionService.currentSelection.value;
final textNodes =
service.selectionService.currentSelectedNodes.whereType<TextNode>();
if (selection == null || textNodes.isEmpty) {
return;
}
final transaction = this.transaction;
transaction.insertNode(
selection.end.path,
Node(
type: _convertPageType(viewPB),
attributes: {
kAppID: appPB.id,
kViewID: viewPB.id,
},
),
);
apply(transaction);
}
String _convertPageType(ViewPB viewPB) {
switch (viewPB.layout) {
case ViewLayoutTypePB.Grid:
return kGridType;
case ViewLayoutTypePB.Board:
return kBoardType;
default:
throw Exception('Unknown layout type');
}
}
}
@@ -0,0 +1,186 @@
import 'package:app_flowy/workspace/application/app/app_service.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/app.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:dartz/dartz.dart' as dartz;
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flutter/material.dart';
import 'insert_page_command.dart';
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:easy_localization/easy_localization.dart';
EditorState? _editorState;
OverlayEntry? _linkToPageMenu;
void showLinkToPageMenu(
EditorState editorState,
SelectionMenuService menuService,
BuildContext context,
ViewLayoutTypePB pageType,
) {
final aligment = menuService.alignment;
final offset = menuService.offset;
menuService.dismiss();
_editorState = editorState;
String hintText = '';
switch (pageType) {
case ViewLayoutTypePB.Grid:
hintText = LocaleKeys.document_slashMenu_grid_selectAGridToLinkTo.tr();
break;
case ViewLayoutTypePB.Board:
hintText = LocaleKeys.document_slashMenu_board_selectABoardToLinkTo.tr();
break;
default:
throw Exception('Unknown layout type');
}
_linkToPageMenu?.remove();
_linkToPageMenu = OverlayEntry(builder: (context) {
return Positioned(
top: aligment == Alignment.bottomLeft ? offset.dy : null,
bottom: aligment == Alignment.topLeft ? offset.dy : null,
left: offset.dx,
child: Material(
color: Colors.transparent,
child: LinkToPageMenu(
editorState: editorState,
layoutType: pageType,
hintText: hintText,
onSelected: (appPB, viewPB) {
editorState.insertPage(appPB, viewPB);
},
),
),
);
});
Overlay.of(context)?.insert(_linkToPageMenu!);
editorState.service.selectionService.currentSelection
.addListener(dismissLinkToPageMenu);
}
void dismissLinkToPageMenu() {
_linkToPageMenu?.remove();
_linkToPageMenu = null;
_editorState?.service.selectionService.currentSelection
.removeListener(dismissLinkToPageMenu);
_editorState = null;
}
class LinkToPageMenu extends StatefulWidget {
const LinkToPageMenu({
super.key,
required this.editorState,
required this.layoutType,
required this.hintText,
required this.onSelected,
});
final EditorState editorState;
final ViewLayoutTypePB layoutType;
final String hintText;
final void Function(AppPB appPB, ViewPB viewPB) onSelected;
@override
State<LinkToPageMenu> createState() => _LinkToPageMenuState();
}
class _LinkToPageMenuState extends State<LinkToPageMenu> {
EditorStyle get style => widget.editorState.editorStyle;
@override
Widget build(BuildContext context) {
return Container(
color: Colors.transparent,
width: 300,
child: Container(
padding: const EdgeInsets.fromLTRB(10, 6, 10, 6),
decoration: BoxDecoration(
color: style.selectionMenuBackgroundColor,
boxShadow: [
BoxShadow(
blurRadius: 5,
spreadRadius: 1,
color: Colors.black.withOpacity(0.1),
),
],
borderRadius: BorderRadius.circular(6.0),
),
child: _buildListWidget(context),
),
);
}
Widget _buildListWidget(BuildContext context) {
return FutureBuilder<List<dartz.Tuple2<AppPB, List<ViewPB>>>>(
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
final apps = snapshot.data;
final children = <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: FlowyText.regular(
widget.hintText,
fontSize: 10,
color: Colors.grey,
),
),
];
if (apps != null && apps.isNotEmpty) {
for (final app in apps) {
if (app.value2.isNotEmpty) {
children.add(
Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: FlowyText.regular(
app.value1.name,
),
),
);
for (final value in app.value2) {
children.add(
FlowyButton(
leftIcon: svgWidget(
_iconName(value),
color: Theme.of(context).colorScheme.onSurface,
),
text: FlowyText.regular(value.name),
onTap: () => widget.onSelected(app.value1, value),
),
);
}
}
}
}
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: children,
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
future: AppService().fetchViews(widget.layoutType),
);
}
String _iconName(ViewPB viewPB) {
switch (viewPB.layout) {
case ViewLayoutTypePB.Grid:
return 'editor/grid';
case ViewLayoutTypePB.Board:
return 'editor/board';
default:
throw Exception('Unknown layout type');
}
}
}
@@ -0,0 +1,29 @@
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/base/link_to_page_widget.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
import 'package:flutter/material.dart';
SelectionMenuItem boardMenuItem = SelectionMenuItem(
name: () => LocaleKeys.document_plugins_referencedBoard.tr(),
icon: (editorState, onSelected) {
return svgWidget(
'editor/board',
size: const Size.square(18.0),
color: onSelected
? editorState.editorStyle.selectionMenuItemSelectedIconColor
: editorState.editorStyle.selectionMenuItemIconColor,
);
},
keywords: ['board', 'kanban'],
handler: (editorState, menuService, context) {
showLinkToPageMenu(
editorState,
menuService,
context,
ViewLayoutTypePB.Board,
);
},
);
@@ -0,0 +1,54 @@
import 'package:app_flowy/plugins/board/presentation/board_page.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/base/built_in_page_widget.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/base/insert_page_command.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
const String kBoardType = 'board';
class BoardNodeWidgetBuilder extends NodeWidgetBuilder<Node> {
@override
Widget build(NodeWidgetContext<Node> context) {
return _BoardWidget(
key: context.node.key,
node: context.node,
editorState: context.editorState,
);
}
@override
NodeValidator<Node> get nodeValidator => (node) {
return node.attributes[kViewID] is String &&
node.attributes[kAppID] is String;
};
}
class _BoardWidget extends StatefulWidget {
const _BoardWidget({
Key? key,
required this.node,
required this.editorState,
}) : super(key: key);
final Node node;
final EditorState editorState;
@override
State<_BoardWidget> createState() => _BoardWidgetState();
}
class _BoardWidgetState extends State<_BoardWidget> {
@override
Widget build(BuildContext context) {
return BuiltInPageWidget(
node: widget.node,
editorState: widget.editorState,
builder: (viewPB) {
return BoardPage(
key: ValueKey(viewPB.id),
view: viewPB,
);
},
);
}
}
@@ -0,0 +1,29 @@
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/base/link_to_page_widget.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
import 'package:flutter/material.dart';
SelectionMenuItem gridMenuItem = SelectionMenuItem(
name: () => LocaleKeys.document_plugins_referencedGrid.tr(),
icon: (editorState, onSelected) {
return svgWidget(
'editor/grid',
size: const Size.square(18.0),
color: onSelected
? editorState.editorStyle.selectionMenuItemSelectedIconColor
: editorState.editorStyle.selectionMenuItemIconColor,
);
},
keywords: ['grid'],
handler: (editorState, menuService, context) {
showLinkToPageMenu(
editorState,
menuService,
context,
ViewLayoutTypePB.Grid,
);
},
);
@@ -0,0 +1,54 @@
import 'package:app_flowy/plugins/document/presentation/plugins/base/built_in_page_widget.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/base/insert_page_command.dart';
import 'package:app_flowy/plugins/grid/presentation/grid_page.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
const String kGridType = 'grid';
class GridNodeWidgetBuilder extends NodeWidgetBuilder<Node> {
@override
Widget build(NodeWidgetContext<Node> context) {
return _GridWidget(
key: context.node.key,
node: context.node,
editorState: context.editorState,
);
}
@override
NodeValidator<Node> get nodeValidator => (node) {
return node.attributes[kAppID] is String &&
node.attributes[kViewID] is String;
};
}
class _GridWidget extends StatefulWidget {
const _GridWidget({
Key? key,
required this.node,
required this.editorState,
}) : super(key: key);
final Node node;
final EditorState editorState;
@override
State<_GridWidget> createState() => _GridWidgetState();
}
class _GridWidgetState extends State<_GridWidget> {
@override
Widget build(BuildContext context) {
return BuiltInPageWidget(
node: widget.node,
editorState: widget.editorState,
builder: (viewPB) {
return GridPage(
key: ValueKey(viewPB.id),
view: viewPB,
);
},
);
}
}
@@ -0,0 +1,13 @@
import 'package:appflowy_editor/appflowy_editor.dart';
class DividerNodeParser extends NodeParser {
const DividerNodeParser();
@override
String get id => 'divider';
@override
String transform(Node node) {
return '---\n';
}
}
@@ -0,0 +1,13 @@
import 'package:appflowy_editor/appflowy_editor.dart';
class MathEquationNodeParser extends NodeParser {
const MathEquationNodeParser();
@override
String get id => 'math_equation';
@override
String transform(Node node) {
return '\$\$${node.attributes[id]}\$\$';
}
}
@@ -0,0 +1,139 @@
library document_plugin;
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/plugins/document/application/share_bloc.dart';
import 'package:app_flowy/workspace/presentation/home/toast.dart';
import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:clipboard/clipboard.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flowy_infra_ui/widget/rounded_button.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DocumentShareButton extends StatelessWidget {
final ViewPB view;
DocumentShareButton({Key? key, required this.view})
: super(key: ValueKey(view.hashCode));
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => getIt<DocShareBloc>(param1: view),
child: BlocListener<DocShareBloc, DocShareState>(
listener: (context, state) {
state.map(
initial: (_) {},
loading: (_) {},
finish: (state) {
state.successOrFail.fold(
_handleExportData,
_handleExportError,
);
},
);
},
child: BlocBuilder<DocShareBloc, DocShareState>(
builder: (context, state) => ConstrainedBox(
constraints: const BoxConstraints.expand(
height: 30,
width: 100,
),
child: ShareActionList(view: view),
),
),
),
);
}
void _handleExportData(ExportDataPB exportData) {
switch (exportData.exportType) {
case ExportType.Link:
break;
case ExportType.Markdown:
FlutterClipboard.copy(exportData.data)
.then((value) => Log.info('copied to clipboard'));
break;
case ExportType.Text:
break;
}
}
void _handleExportError(FlowyError error) {}
}
class ShareActionList extends StatelessWidget {
const ShareActionList({
Key? key,
required this.view,
}) : super(key: key);
final ViewPB view;
@override
Widget build(BuildContext context) {
final docShareBloc = context.read<DocShareBloc>();
return PopoverActionList<ShareActionWrapper>(
direction: PopoverDirection.bottomWithCenterAligned,
actions: ShareAction.values
.map((action) => ShareActionWrapper(action))
.toList(),
buildChild: (controller) {
return RoundedTextButton(
title: LocaleKeys.shareAction_buttonText.tr(),
onPressed: () => controller.show(),
);
},
onSelected: (action, controller) async {
switch (action.inner) {
case ShareAction.markdown:
final exportPath = await FilePicker.platform.saveFile(
dialogTitle: '',
fileName: '${view.name}.md',
);
if (exportPath != null) {
docShareBloc.add(DocShareEvent.shareMarkdown(exportPath));
showMessageToast('Exported to: $exportPath');
}
break;
case ShareAction.copyLink:
NavigatorAlertDialog(
title: LocaleKeys.shareAction_workInProgress.tr(),
).show(context);
break;
}
controller.close();
},
);
}
}
enum ShareAction {
markdown,
copyLink,
}
class ShareActionWrapper extends ActionCell {
final ShareAction inner;
ShareActionWrapper(this.inner);
Widget? icon(Color iconColor) => null;
@override
String get name {
switch (inner) {
case ShareAction.markdown:
return LocaleKeys.shareAction_markdown.tr();
case ShareAction.copyLink:
return LocaleKeys.shareAction_copyLink.tr();
}
}
}
@@ -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,7 +1,7 @@
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:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database/notification.pb.dart';
import 'package:flowy_infra/notifier.dart';
import 'dart:async';
import 'dart:typed_data';
@@ -13,18 +13,18 @@ class CellListener {
final String fieldId;
PublishNotifier<UpdateFieldNotifiedValue>? _updateCellNotifier =
PublishNotifier();
GridNotificationListener? _listener;
DatabaseNotificationListener? _listener;
CellListener({required this.rowId, required this.fieldId});
void start({required void Function(UpdateFieldNotifiedValue) onCellChanged}) {
_updateCellNotifier?.addPublishListener(onCellChanged);
_listener = GridNotificationListener(
_listener = DatabaseNotificationListener(
objectId: "$rowId:$fieldId", handler: _handler);
}
void _handler(GridDartNotification ty, Either<Uint8List, FlowyError> result) {
void _handler(DatabaseNotification ty, Either<Uint8List, FlowyError> result) {
switch (ty) {
case GridDartNotification.DidUpdateCell:
case DatabaseNotification.DidUpdateCell:
result.fold(
(payload) => _updateCellNotifier?.value = left(unit),
(error) => _updateCellNotifier?.value = right(error),
Loaded 100 of 1551 files, more files were not shown because too many files have changed in this diff. Show more