Compare commits

...

220 Commits
0.6.2 ... main

Author SHA1 Message Date
Nathan.fooo
8139065113
fix: init database row init (#6127) 2024-08-30 22:12:20 +08:00
Aymane Boumaaza
3324e7837b
chore: update ar-SA and fr-FR translations with Fink 🐦 (#6084)
Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2024-08-30 21:22:01 +08:00
vavenCV
f20f8bcfbf
chore: update Fr-fr translations (#6106)
* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2024-08-30 21:17:42 +08:00
MIckael
c20ed8c019
chore: update Pt-br translations (#6107)
* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2024-08-30 20:14:04 +08:00
Yurmin
0b658bff0b
chore: update it-IT translations (#6058)
* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2024-08-30 20:13:33 +08:00
Abdulraaof
8319606cc0
chore: updated Arabic translation (#6111)
Co-authored-by: Nathan.fooo <86001920+appflowy@users.noreply.github.com>
Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2024-08-30 20:13:17 +08:00
Antonio Albert
eefdf96b00
fix: typo in german translation (#6126) 2024-08-30 19:31:15 +08:00
KD-MM2
e85e092db8
chore: update translations for Vietnamese(vi-VN) (#6122)
* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦
2024-08-30 19:24:43 +08:00
Lucas.Xu
928da2a223
chore: update translations (#6124)
* chore: update translations

* fix: close popup menu when tapping navigation bar item

* chore: update toolbar divider color and popup menu background color

* chore: update translations and icon

* chore: update frontend/appflowy_flutter/lib/shared/popup_menu/appflowy_popup_menu.dart

Co-authored-by: Mathias Mogensen <42929161+Xazin@users.noreply.github.com>

---------

Co-authored-by: Mathias Mogensen <42929161+Xazin@users.noreply.github.com>
2024-08-30 19:19:41 +08:00
Mathias Mogensen
78c2e756d6
fix: lose focus when changing tabs (#6118) 2024-08-30 10:12:36 +02:00
Mathias Mogensen
47c2ae23ed
fix: move title bar on top of tabs on Windows (#6116) 2024-08-30 10:12:26 +02:00
Mathias Mogensen
0fd0900b41
fix: add leading zeros to day & month in date format (#6114) 2024-08-30 10:12:16 +02:00
Lucas.Xu
61ad75502f
feat: upgrade template button style (#6121) 2024-08-30 16:10:17 +08:00
Nathan.fooo
29858dda7a
chore: update plan desc (#6120) 2024-08-30 15:35:57 +08:00
koukemo
34c441f3ad
fix: add libnotify and rocksdb as docker dependencies (#6065) 2024-08-30 15:28:09 +08:00
Nathan.fooo
c3114e5a39
chore: bump client api (#6113)
* chore: bump client api and collab
2024-08-30 14:31:05 +08:00
Nathan.fooo
d89804f3e4
chore: bump client api (#6098) 2024-08-28 21:31:04 +08:00
Lucas.Xu
9209562648
fix: reset first page when switching space (#6097)
* fix: reset first page when switching space

* fix: primary field icon align issue in grid page
2024-08-28 21:25:57 +08:00
Lucas.Xu
9ee8cc6a7b
feat: optimize sync error page (#6082) 2024-08-28 18:53:16 +08:00
Nathan.fooo
9a295daf99
chore: fix database filter (#6094)
* chore: fix database filter

* chore: fix test
2024-08-28 16:15:40 +08:00
Lucas.Xu
956d62fe82
fix: expand the icon to be the same size as the text in the heading block (#6093)
* fix: hidden board group padding

* fix: expand icon in heading style
2024-08-28 13:22:07 +08:00
Kilu.He
7541dff00e
fix: some file block issues (#6085) 2024-08-28 10:30:08 +08:00
Nathan.fooo
c4cdcbff73
chore: disable old import type (#6089) 2024-08-28 09:15:42 +08:00
Bartosz Sypytkowski
b77fdb8424
chore: update yrs dependencies (#6086)
* chore: update dependencies

* chore: catch results from apply update

---------

Co-authored-by: nathan <nathan@appflowy.io>
2024-08-27 20:53:00 +08:00
Nathan.fooo
083c0d0f0b
chore: upgrade client api that fix initfine sync (#6083) 2024-08-27 19:59:52 +08:00
Kilu.He
d25efba292
feat: support file block preview on web (#6081) 2024-08-27 15:01:23 +08:00
Nathan.fooo
40e627c303
chore: enable upload when switching workspace (#6080)
* chore: enable upload when switching workspace

* chore: update client api
2024-08-27 14:22:57 +08:00
Kilu.He
e3a68d3ecb
fix: change icons (#6076) 2024-08-27 10:59:31 +08:00
Lucas.Xu
1b185ba3cd
feat: support sign in with Apple (#6049)
* feat: support sign in with Apple

* feat: support sign in with Apple

* feat: optimize sign in on desktop

* feat: expand third party sign in buttons on android

* fix: revert text color and font size in button
2024-08-27 10:49:31 +08:00
Lucas.Xu
e5ad0f6d1d
fix: table render error (#6074)
* fix: table render error

* fix: sidebar doesn't refresh if the pages are same

* chore: optimize empty placeholder align

* fix: debounce open workspace toast

* fix: editor color decoration error
2024-08-27 09:23:25 +08:00
Nathan.fooo
93bf1f79f6
chore: fix database row meta (#6078)
* chore: fix database row meta

* chore: update

* chore: update client api

* chore: clippy

* chore: update client api

* chore: update collab

* chore: update client api
2024-08-26 23:42:18 +08:00
Lucas.Xu
f342f5ec7e
feat: customize transition animation for popup menu (#6071)
* feat: customize transition animation for popup menu

* chore: add hint text for duplicate and favorite action

* fix: flutter analyze
2024-08-26 15:15:59 +08:00
Nathan.fooo
12cb9bde39
chore: replace cell content when import from csv (#6073) 2024-08-26 14:19:30 +08:00
Kilu.He
b649950d62
fix: table issues (#6072) 2024-08-26 13:52:02 +08:00
Lucas.Xu
2ef74c229c
fix: update settings menu color (#6039)
* chore: replace settings popup menu backgroundcolor

* fix: invite toast blocked by keyboard
2024-08-26 09:51:57 +08:00
Nathan.fooo
62f0307289
feat: async load database row, async filter, async sort (#6068)
* chore: display date when convert text to date

* chore: filter & sort

* chore: fix filter and sort

* chore: fix test

* chore: clippy

* chore: fix test
2024-08-26 09:46:16 +08:00
Kilu.He
242faee2f5
fix: document issues (#6069) 2024-08-25 22:06:34 +08:00
Kilu.He
7626cfd546
fix: database tabs (#6067) 2024-08-25 21:45:01 +08:00
Kilu.He
5b2df9e482
fix: calendar style issues (#6066) 2024-08-25 20:56:41 +08:00
Kilu.He
2a2dc903c1
fix: table cells order on publish (#6064) 2024-08-25 17:27:44 +08:00
Nathan.fooo
d1ed45c312
fix: upload all imported csv rows (#6063)
* chore: upload imported csv rows

* chore: format number
2024-08-25 15:14:54 +08:00
Nathan.fooo
a487aa74fd
feat: support edit imported database (#6061)
* chore: change field type of imported csv

* fix: support load 10000 rows

* fix: clippy
2024-08-25 14:28:51 +08:00
Nathan.fooo
d3b7c5fea5
chore: async load database row (#6051)
* chore: update

* chore: fix test

* chore: clippy

* chore: clippy
2024-08-24 00:40:57 +08:00
Kilu.He
3fa72106e9
fix: upload template (#6021)
* fix: upload template

* fix: scale thumb
2024-08-23 16:28:46 +08:00
Nathan.fooo
8ae67c5098
chore: upgrade collab verison (#6047) 2024-08-23 15:40:48 +08:00
Nathan.fooo
a206d9aa8c
chore: fix board row (#6046)
* chore: fix board row init

* chore: update client api version
2024-08-23 13:55:40 +08:00
Nathan.fooo
9e93483113
chore: Enable document undo redo (#6044)
* chore: update logs

* chore: enable document undo redo

* chore: clippy
2024-08-22 22:31:43 +08:00
Lucas.Xu
00690a1bb8
chore: add images from readme (#6041) 2024-08-22 17:51:44 +08:00
Nathan.fooo
86be92ba1b
chore: upgrade collab version (#6038) 2024-08-22 16:28:29 +08:00
Khor Shu Heng
6305ab8c5d
chore: bump version to 0.6.8 (#6037) 2024-08-22 12:33:13 +08:00
Lucas.Xu
c371c6cd63
fix: ai writer generate button color (#6035)
* fix: ai writer generate button color

* fix: replace ai writer button with outlined rounded button
2024-08-22 11:38:50 +08:00
Lucas.Xu
190e3bedda
chore: update member exceeded hint text (#6034)
* chore: update member exceeded hint text

* fix: disable ai writer widget on mobile
2024-08-22 11:14:01 +08:00
Francisco Di Marzo Borghi
a78752d427
fix: removed Wayland Header (#5991)
* fix: sync AppImage recipe linux version with release

* fix: don't draw custom header on Wayland sessions

* chore(AppImage): reset `app_info.version`
2024-08-22 09:36:26 +08:00
Evililim
b2b72d2130
chore: update French translations (#5973)
* chore: update translations with Fink 🐦

* Corrections demandée par Ninja i18n

* Chore: Correction for Ninja i18n
2024-08-22 09:34:06 +08:00
Lucas.Xu
104bf12ac7
feat: optimize settings on mobile (#6031)
* feat: optimize settings on mobile

* feat: open discord link when clicking help button

* fix: flutter analyze
2024-08-22 09:32:29 +08:00
Bartosz Sypytkowski
0e844678fc
chore: Remove last sync at (#6029)
* chore: update dependencies to stop using last sync at field

* chore: apply cargo fmt

* chore: fix dependencies

* chore: upgrade client api

---------

Co-authored-by: nathan <nathan@appflowy.io>
2024-08-22 08:42:32 +08:00
Nathan.fooo
23968d89fc
chore: Upgrade collab version (#6028)
* chore: write collab to disk if it's not exist

* chore: write collab if it's not exit

* chore: fix test

* chore: upgrade appflowy collab

* chore: upgrade appflowy collab

* chore: update collab version

* chore: fix test
2024-08-22 07:45:51 +08:00
Nathan.fooo
0ce43ca5fa
chore: write collab to disk if it's not exist (#6023)
* chore: write collab to disk if it's not exist

* chore: write collab if it's not exit

* chore: fix test
2024-08-21 14:04:53 +08:00
nathan
b9a34f6fc2 chore: turn off verbose log 2024-08-20 21:45:14 +08:00
Nathan.fooo
93a110d37d
chore: explicit using any int64 (#6020)
* chore: explicit using any int64

* chore: update commit id of appflowy collab
2024-08-20 21:38:29 +08:00
Nathan.fooo
70d6351a6c
chore: calm clippy wanring when using non send with Arc (#6018) 2024-08-20 17:07:54 +08:00
Lucas.Xu
70e96c01b3
fix: ignore case sensitive of image name when dragging image to document (#6017) 2024-08-20 16:55:53 +08:00
Nathan.fooo
6a0650e6d5
chore: fix file upload test (#6016)
* chore: fix file upload test
2024-08-20 15:18:57 +08:00
Nathan.fooo
6d09c33782
chore: spawn task on local set (#6012)
* chore: spawn local

* chore: using multiple thread runtime

* chore: fix test
2024-08-20 14:16:24 +08:00
nathan
faf1e98d15 chore: update appflowy collab 2024-08-19 22:09:31 +08:00
Nathan.fooo
58b17a939c
chore: fix lib dispatch (#6008)
* chore: replace rc with arc

* chore: fix dispatch

* chore: fix test

* chore: fix dispatch

* chore: fix test

* chore: remove afconcurrent

* chore: fix runtime block_on runtime
2024-08-19 22:08:10 +08:00
Lucas.Xu
7113269802
chore: optimize row card page UI (#5995)
* chore: adjust buttons padding in row record page

* fix: disable more button in row page

* fix: upload image button ui on mobile

* fix: embed link button ui on mobile

* fix: add missing border for ai text field and ai translate field

* fix: delete AI can make mistakes on mobile

* chore: disable sentry

* fix: invite error toast

* fix: add member limit hint text in invite member screen

* feat: show toast after opening workspace on mobile

* chore: remove sentry

* chore: filter row page in recent views

* feat: support display field name as row page title

* chore: remove scroll bar on home page

* chore: remove legacy code

* chore: optimize mobile speed

* Revert "chore: remove sentry"

This reverts commit 73b45e2590655a992cec409503c0693df845914e.

* fix: reduce document page rebuild time

* chore: improve tooltip style
2024-08-19 11:06:34 +08:00
Lucas.Xu
e460120a1c
feat: add ai bubble button on mobile home page (#5992)
* chore: skip check list test if the task is not found

* feat: add ai bubble button in home page

* feat: only show the ai bubble button for the cloud user

* chore: add border color to ai bubble button

* Revert "chore: skip check list test if the task is not found"

This reverts commit 961f594a31906c52384c09915dce8f9db7fbd5bc.

* fix: only display ai bubble button on home page
2024-08-19 09:50:42 +08:00
Nathan.fooo
d0ce65f711
chore: fix database filter (#6005)
* chore: fix filter
2024-08-19 09:47:37 +08:00
Nathan.fooo
5878379b2e
chore: lazy load database row (#6001)
* chore: fix potential load database rows fail

* chore: fix layout padding
2024-08-18 18:44:32 +08:00
Bartosz Sypytkowski
fd5299a13d
move to latest appflowy collab version (#5894)
* chore: move to latest appflowy collab version

* chore: filter mapping

* chore: remove mutex folder

* chore: cleanup borrow checker issues

* chore: fixed flowy user crate compilation errors

* chore: removed parking lot crate

* chore: adjusting non locking approach

* chore: remove with folder method

* chore: fix folder manager

* chore: fixed workspace database compilation errors

* chore: initialize database plugins

* chore: fix locks in flowy core

* chore: remove supabase

* chore: async traits

* chore: add mutexes in dart ffi

* chore: post rebase fixes

* chore: remove supabase dart code

* chore: fix deadlock

* chore: fix page_id is empty

* chore: use data source to init collab

* chore: fix user awareness test

* chore: fix database deadlock

* fix: initialize user awareness

* chore: fix open workspace test

* chore: fix import csv

* chore: fix update row meta deadlock

* chore: fix document size test

* fix: timestamp set/get type convert

* fix: calculation

* chore: revert Arc to Rc

* chore: attach plugin to database and database row

* chore: async get row

* chore: clippy

* chore: fix tauri build

* chore: clippy

* fix: duplicate view deadlock

* chore: fmt

* chore: tauri build

---------

Co-authored-by: nathan <nathan@appflowy.io>
2024-08-18 11:16:42 +08:00
Nathan.fooo
c2d7c5360d
chore: remove file size limit (#5974)
* chore: remove file size limit

* chore: flutter analyze
2024-08-17 21:51:36 +08:00
Lucas.Xu
9853fbfc10
chore: support monochrome icon on Android (#5989) 2024-08-17 11:05:06 +08:00
Lucas.Xu
44fb610269
fix: support pasting web image on mobile (#5987)
* fix: support pasting web image on mobile

* fix: permission check will deactive editor focus
2024-08-17 11:04:56 +08:00
Lucas.Xu
e6bf6a5c7d
feat: support inviting members on mobile (#5986)
* feat: support inviting members on mobile

* feat: support workspace member list on mobile

* feat: support leave workspace on mobile

* chore: adjust member list ui

* fix: flutter analyze
2024-08-17 11:04:43 +08:00
Lucas.Xu
d3d929b68e
fix: unable to insert todo list via slash menu (#5980)
* fix: unable to insert todo list via slash menu

* fix: unable to insert divider via slash menu

* chore: update editor version

* chore: update translations

* chore: decrease sentry sample rate to 0.1

* fix: integration test
2024-08-16 11:58:48 +08:00
Lucas.Xu
f7a2d9e581
chore: add loading indicator when generating freezed file (#5978) 2024-08-15 20:12:25 +08:00
Lucas.Xu
6283649a6b
feat: open the row page on mobile (#5975)
* chore: add dart dependency validator

* feat: open the row page on mobile

* Revert "chore: add dart dependency validator"

This reverts commit c81e5ef0ed7b0f1e74d6ba499722a9e2b566862f.

* chore: update translations

* feat: preload row page to reduce open time

* chore: don't add orphan doc into recent records

* fix: bloc error

* fix: migrate the row page title to latest design

* chore: optimize database mobile UI
2024-08-15 20:12:09 +08:00
Lucas.Xu
88cc0caab7
feat: integrate Sentry Flutter and enable it if SENTRY_DSN is not empty (#5959)
* chore: add dart dependency validator

* feat: integrate sentry flutter

* chore: remove user info collection

* fix: flutter analyze

* fix: ios compile

* chore: add log
2024-08-15 10:00:27 +08:00
Nathan.fooo
7eb8ea347d
fix: local ai toggle (#5968)
* chore: fix disable local ai

* chore: do not index file
2024-08-15 07:44:32 +08:00
Nathan.fooo
8935b7158c
chore: remove workspac id in user profile (#5962)
* chore: remove workspac id in user profile

* chore: fix test

* chore: clippy

* chore: clippy

* chore: fix cloud test

* chore: fix checklist test
2024-08-14 19:44:15 +08:00
Nathan.fooo
fa230907ca
fix: refresh local ai state when opening workspace (#5961)
* chore: fix local ai state when open other workspace

* chore: fix duplicate message
2024-08-14 16:58:56 +08:00
Nathan.fooo
6d496b2088
chore: remove future result (#5960)
* chore: remove future result

* chore: fix test
2024-08-14 15:50:21 +08:00
Nathan.fooo
4b24b41dd4
chore: rm fut (#5958)
* chore: rm fut

* chore: clippy
2024-08-14 10:33:23 +08:00
Lucas.Xu
b3a0119c18
feat: optimize date picker & mention block (#5954)
* chore: optimize rename button on mobile

* fix: mention block id empty error

* chore: optimize mention block style

* feat: add confirm button in date picker
2024-08-14 09:31:30 +08:00
Nathan.fooo
463c8c7ee4
feat: enable local set (#5955)
* chore: enable local set

* chore: fix test

* chore: clippy

* chore: fix tauri build

* chore: fix tauri build
2024-08-13 23:36:44 +08:00
Lucas.Xu
e2359cf047
fix: row property align issue (#5950)
* fix: row property align issue

* fix: generate_freezed.sh path warning

* Revert "fix: generate_freezed.sh path warning"

This reverts commit 7c0a4a3177702d5b58858296133876a04ebe274d.

* fix: generate_freezed.sh path warning

* chore: improve chat page mobile UI
2024-08-13 20:01:32 +08:00
Lucas.Xu
d23977ebb0
chore: optimize generation speed (#5930)
* chore: optimize language generation speed

* chore: optimize code generation speed

* chore: optimize freezed generation speed

* chore: optimzie appflowy_backend generation speed

* chore: optimize appflowy_result

* chore: optimzie flowy_infra generation speed

* chore: optimzie flowy_infra_ui generation speed

* chore: optimzie appflowy_flutter generation speed

* chore: optimize generate.sh

* chore: optimize the execution order

* chore: use exclude_packages instead of include_packages
2024-08-13 16:13:33 +08:00
Lucas.Xu
f1ad03eaa9
chore: bump version 0.6.7 (#5949)
* chore: update collab version

* chore: update changelog

* chore: update notification reddot style

* fix: unable to fetch reminders

* chore: optimize notification red dot size
2024-08-13 15:03:57 +08:00
Lucas.Xu
17c9c9b556
chore: optimize recent section performance (#5948)
* fix: duplicated call for setting recent view

* chore: reduce recent update
2024-08-13 13:52:02 +08:00
Lucas.Xu
93f9a2cab1
feat: display no access page (#5941)
* feat: display no access page

* fix: optimize the primary rounded button
2024-08-13 09:32:22 +08:00
Annie
4b710527c9
chore: remove file upload limitation & add tooltip for search (#5944) 2024-08-12 22:24:24 +08:00
Nathan.fooo
6e26dc128c
chore: fix message id (#5943) 2024-08-12 22:24:06 +08:00
Annie
d3d9ab2fb0
chore: reset line height to null as default value (#5942) 2024-08-12 18:08:54 +08:00
Nathan.fooo
1db8480b75
chore: using write lock to fix acquire lock error (#5938) 2024-08-12 16:28:34 +08:00
Annie
7e53b34484
chore: center confirm or cancel (#5936)
Co-authored-by: Nathan.fooo <86001920+appflowy@users.noreply.github.com>
2024-08-12 16:19:16 +08:00
Lucas.Xu
34465efc24
chore: replace all the buttons on settings page with the primary button style (#5937) 2024-08-12 16:03:52 +08:00
Annie
a29b170b13
chore: remove stability ai (#5927)
* chore: remove stability ai

* chore: remove stabilityAI widgets

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2024-08-12 15:46:56 +08:00
Mohammad Zolfaghari
14b60fb9b0
fix: list paste bug #5846 (#5917)
* fix: list paste bug #5846

selecting a single word on a bullet or number list and pasting it
from the clipboard would clear the whole text of that node and
replace it with clipboard text.

resolves #5846

* chore: update editor version

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2024-08-12 15:46:45 +08:00
Nathan.fooo
1c638dd930
chore: show reference sources on local ai response (#5933)
* chore: show reference sources on local ai response

* chore: clippy

* chore: clippy

* chore: update i18n

* chore: update client api
2024-08-12 15:43:17 +08:00
Sota
55a4810d60
chore: update translations with Fink 🐦 (#5932) 2024-08-12 15:12:52 +08:00
Annie
2bc7875bdd
chore: polish upload image menu (#5928)
* chore: polish upload image menu

* fix: text align issue

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2024-08-12 13:52:53 +08:00
Nathan.fooo
f7adcae8ff
chore: show attachment on local ai (#5929)
* chore: show attachment on local ai

* chore: fix compile
2024-08-12 09:21:44 +08:00
Nathan.fooo
23997e977c
refactor: revamp file upload and fix partitial upload bugs (#5924)
* chore: upload chat file to local ai

* chore: async func

* chore: individual file progress

* chore: fix test

* chore: fix file upload
2024-08-11 20:39:25 +08:00
Lucas.Xu
3ff47b7e1e
fix: link to two calendars in same doc may failed randomly (#5926)
* chore: udpate translation

* fix: editor loses focus randomly when interacting with certain databases

* fix: text align issues on Windows

* chore: update editor version
2024-08-11 20:18:35 +08:00
Annie
d33af70a5c
chore: revamp editor icon (#5925)
* chore: revamp file block icon

* chore: revamp other blocks icon

* fix: flutter analyze

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2024-08-11 20:18:23 +08:00
nathan
510752868d chore: fix flutter wanrings 2024-08-11 15:16:02 +08:00
nathan
42d1bb84c5 chore: fix compile 2024-08-11 15:10:30 +08:00
Annie
2debd0283c
chore: revamp question bubble (#5923) 2024-08-11 15:02:29 +08:00
Nathan.fooo
e87ade6b3f
chore: update tabbar icon (#5921) 2024-08-10 21:06:54 +08:00
Lucas.Xu
e2a923a796
chore: update editor version (#5918)
* chore: update editor version

* fix: new property field width on Mobile

* feat: support enter to insert a new line on mobile

* feat: optimzie callout style

* feat: add hover effect on share button

* chore: fix

---------

Co-authored-by: Nathan.fooo <86001920+appflowy@users.noreply.github.com>
Co-authored-by: nathan <nathan@appflowy.io>
2024-08-10 20:49:06 +08:00
Annie
cd0f8d80e9
chore: replace icons (#5914)
Co-authored-by: nathan <nathan@appflowy.io>
2024-08-10 18:05:53 +08:00
Nathan.fooo
7abe9f4661
feat: show indicator when send chat message with attachment/mention etc (#5919)
* chore: adjust line height

* chore: send stream message

* chore: index file

* chore: clippy
2024-08-10 17:23:37 +08:00
Nathan.fooo
758c304a74
feat: support mention a document as context on local ai (#5913)
* feat: support mention a document as context on local ai

* chore: rename

* chore: fix test

* chore: fix test
2024-08-09 21:55:20 +08:00
Stefan Weiberg
f84473c857
chore: update German translations to state of v0.6.7 (#5848)
* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* fix: add empty itemSeven removed by Inlang

* fix: add empty itemSeven removed by Inlang

fix for turkish translation

* fix: empty translation for itemSeven

quickfix for the empty translation of itemSeven. This has been done the same way to resolve previous issues with InLang and empty translation strings.

* fix: empty translation for itemSeven in TR

quickfix for the empty translation of itemSeven. This has been done the same way to resolve previous issues with InLang and empty translation strings.

* fix: remove itemSeven completely

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* fix: resolve conflicts after merging main

* chore: update translations with Fink 🐦

---------

Co-authored-by: Mathias Mogensen <mathiasrieckm@gmail.com>
Co-authored-by: Mathias Mogensen <mathias@appflowy.io>
2024-08-09 21:51:23 +08:00
Lucas.Xu
a26b2a356c
feat: optimize the slash menu item name & icon (#5908)
* feat: optimize the slash menu item name & icon

* feat: optimize the toolbar item name & icon

* fix: integration test

* fix: replace unaligned icons
2024-08-09 21:50:47 +08:00
Annie
57bbb6cc41
chore: replace property icons in database (#5910) 2024-08-09 21:08:13 +08:00
Nathan.fooo
e82edc0419
chore: polish UI and display attachment when using local ai (#5906)
* chore: polish UI

* chore: fix compile
2024-08-09 07:40:24 +08:00
Lucas.Xu
f57297e76d
fix: known issues in 0.6.7 (#5903)
* feat: add capitalize extension for string and apply it in emoji picker header and icon picker

* fix: adjust tooltip height calculation

* feat: add Streamline open source icons notice and link

* feat: enhance heading toolbar item to toggle heading level and cancel on same level selection

* feat: use home-3 as default space icon

* feat: use the first character of space name as icon if icon was removed

* chore: update hover effect for delete workspace button

* chore: optimize space icon on mobile

* fix: adjust chat ui on mobile

* fix: adjust default space icon on mobile
2024-08-08 19:31:10 +08:00
Lucas.Xu
7769034467
chore: add more logs in reminder bloc (#5860)
* chore: add more logs in reminder bloc

* Revert "chore: add more logs in reminder bloc"

This reverts commit 9d0bb8fb2963d24cc572754c2f9411cebfa69f98.

* chore: add more logs in reminder bloc

* fix: unable to view reminders on Desktop

* fix: force refresh reminders

* chore: fix flutter analyze

* feat: support database reminder on Mobile

* chore: remove referenced database padding
2024-08-08 13:35:22 +08:00
Nathan.fooo
a523b8ff90
chore: chat ui polish (#5902)
* chore: chat ui polish

* chore: fmt

* chore: clippy
2024-08-08 12:07:00 +08:00
Kilu.He
7b7b907017
feat: support custom icon (#5869)
* feat: support custom icon

* fix: quote

* chore: join lines for icons.json

* fix: manual add .svg for icon

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2024-08-08 09:49:34 +08:00
Lucas.Xu
29b262a1c6
chore: optimize mobile ai chat page (#5897)
* feat: improve chat page UI on mobile

* feat: integrate add page menu into chat page on mobile

* fix: only display document view in @ menu
2024-08-08 09:49:08 +08:00
Nathan.fooo
e28a251e72
chore: chat UI poblish (#5895)
* chore: update ui

* chore: update send state

* chore: workspace owner prompt

* chore: show other user

* chore: fix ui
2024-08-07 16:48:09 +08:00
Lucas.Xu
98b7882d43
feat: move all the heading toolbar items into a popup menu (#5890)
* chore: udpate translations

* feat: move all the heading items into a popup menu

* chore: add arrow down icon after heading toolbar items

* fix: compile

* chore: adjust heading toolbar style
2024-08-07 12:42:52 +08:00
Lucas.Xu
e279ad1cc7
feat: optimize editor toolbar tooltip (#5889)
* chore: improve icon picker dark mode color

* feat: optimize editor toolbar tooltip style

* feat: customize markdown item tooltip message

* chore: find the tooltip by rich message

* feat: add hover effect in toolbar item

* feat: add hover effect in toolbar item

* chore: optimize hover color in light mode

* chore: fix integration test

* chore: optimize align & font toolbar item

* chore: fix integration test
2024-08-07 11:37:30 +08:00
Mathias Mogensen
a798b037db
fix: add back invitation success toast (#5875) 2024-08-06 10:10:06 +02:00
Nathan.fooo
5fbaf664ba
chore: update translation (#5887) 2024-08-06 16:01:32 +08:00
Lucas.Xu
41a346c7ed
chore: adjust legacy space icon (#5886) 2024-08-06 15:10:44 +08:00
Lucas.Xu
aab942d163
fix: remove unused expanded widget (#5885)
* fix: remove unused expanded widget

* Revert "fix: remove unused expanded widget"

This reverts commit dcdc318ea24926075d3e9a3b827e365b0de77826.

* fix: remove unused expanded widget
2024-08-06 14:19:44 +08:00
Nathan.fooo
5757cc9a1d
chore: polish ui (#5883)
* chore: polish ui

* chore: clippy

* chore: fmt
2024-08-06 13:17:56 +08:00
Lucas.Xu
453e6309d5
feat: refactor space icon picker (#5878)
* feat: refactor space icon picker

* chore: optimize the _loadIconGroups function

* feat: refactor emoji picker

* feat: integrate icon picker into flowy_icon_emoji_picker

* feat: support searching icon

* feat: support displaying new icons

* fix: flutter analyze

* chore: join lines

* feat: support space icon in view title

* feat: support customzing icon when creating space or managing space

* feat: customize the emoji picker and icon picker padding

* feat: shuffle icon

* fix: expand popup menu font size

* fix: flutter integration test
2024-08-06 11:47:38 +08:00
Lucas.Xu
4041724980
fix: prevent shaking in nested lists deeper in AI message (#5877) 2024-08-06 11:22:40 +08:00
Nathan.fooo
d378c456d4
feat: Implement menu on chat (#5879)
* chore: implement menu

* chore: gather metadata

* chore: update client api

* chore: stream metadata

* chore: save metadata

* chore: update client api

* chore: adjust UI

* chore: fmt
2024-08-06 07:56:13 +08:00
Lucas.Xu
0abf916796
feat: support pasting image from Slack (#5864) 2024-08-02 22:00:40 +08:00
Lucas.Xu
393850ae4b
chore: expand inline menu width (#5867) 2024-08-02 22:00:28 +08:00
Lucas.Xu
46bad4e7e8
chore: optimize the workspace menu hover status (#5865) 2024-08-02 16:10:49 +08:00
Lucas.Xu
e9fc003e10
fix: + button in the navigation bar doesn't work when the space is not initialized (#5862) 2024-08-02 13:11:52 +08:00
Kilu.He
cb60488bbe
fix: replace wasm with axios (#5856)
* fix: replace wasm with axios

* fix: login redirect

* fix: flag emoji on windows
2024-08-02 12:19:32 +08:00
Lucas.Xu
04556252e1
chore: optimzie emoji align on desktop (#5857) 2024-08-02 10:20:31 +08:00
Mathias Mogensen
c2e8a12427
fix: select option chip size (#5859) 2024-08-01 23:28:05 +02:00
Nathan.fooo
73421e0d58
feat: suppport translate and summary using local ai (#5858)
* chore: fix ui

* chore: fix ui

* chore: rename

* chore: rename

* chore: rename

* chore: refactor database ai interface

* chore: support local ai for database

* chore: clippy
2024-08-01 23:13:35 +08:00
Lucas.Xu
b9fd3701cd
feat: improve reminder color in notification page (#5855)
* feat: improve reminder color

* fix: notification page doesn't update when switching workspace
2024-08-01 20:21:25 +08:00
Lucas.Xu
9fbba5fb60
feat: notification multiple select (#5847)
* chore: update editor version

* feat: support multi select notification items

* fix: flutter analyze

* feat: add navgation bar button

* feat: add multi select item

* feat: add multi choice in notification page

* feat: support multi choice

* chore: update icon

* feat: support open page from notification page

* chore: update version
2024-08-01 16:30:15 +08:00
Lucas.Xu
7261d1e8da
chore: update rocksdb version 0.22.0 (#5826)
* chore: update rocksdb version 0.22.0

* chore: update collab

* chore: revert rust toolchain to 1.77.2
2024-08-01 15:36:59 +08:00
Lucas.Xu
27aac2b911
chore: optimize emoji align on mobile (#5852) 2024-08-01 15:36:21 +08:00
Kilu.He
0cd5af5ffa
fix: add comment tip (#5854) 2024-08-01 14:15:38 +08:00
Kilu.He
87e950733f
fix: react action bugs (#5851) 2024-08-01 13:07:12 +08:00
Kilu.He
2402b4c6f1
feat: support global comment on publish (#5834)
* feat: support duplicate UI on web

* fix: replace google svg

* fix: modified some copy

* fix: adjust modal position

* fix: upgrade wasm package

* fix: text overflow

* fix: global comments

* fix: replace appflowy icon

* fix: demond load outline

* fix: lazy load

* fix: close duplicate entry

* fix: ci error

* fix: modified comment styles

* fix: adjust space

* fix: easy find reply comment

* fix: calendar scroll bugs

* fix: image render

* fix: replace loading

* fix: issues of test session

* fix: fixed adding comment

* fix: database view name
2024-08-01 12:59:04 +08:00
Mathias Mogensen
ed81a0aff2
feat: upload file in document (#5843)
* feat: upload file in document

* feat: add uploaded at & improvements

* fix: popover onOpen not triggered by manual show

* test: add basic file test

* test: fix and add rename test
2024-07-31 15:49:35 +02:00
Lucas.Xu
dce9231118
fix: try to reopen the first workspace if the workspace deletion failed (#5844) 2024-07-31 17:52:36 +08:00
Lucas.Xu
d1c1449cf6
feat: support notification on mobile (#5831)
* feat: add inbox/unread/archived tabs

* feat: dump notification info

* chore: add reminder bloc

* feat: support unread / archive notification tab

* feat: support archive all & mark all as read

* feat: add empty page

* chore: optimize gesture

* feat: add red dot above notification icon

* chore: optimize code logic

* feat: optimize tabbar animation

* fix: notification align issue

* fix: todo list icon align issue

* feat: disable emoji button inside callout in read-only mode

* feat: optimize icon size in editor

* chore: improve text color in dark mode
2024-07-31 15:15:15 +08:00
Nathan.fooo
7c3dd5375d
chore: switch to appflowy ai if local ai is not enable (#5839)
* chore: switch to appflowy ai if local ai is not enable

* chore: clippy

* chore: fix enable bug

* chore: fix compile

* chore: clippy
2024-07-31 11:47:09 +08:00
Lucas.Xu
81532d014e
chore: use light haptic feedback (#5838)
* chore: use light haptic feedback

* chore: optimize tabbar & side action animation
2024-07-30 19:53:11 +08:00
Nathan.fooo
735a09b333
chore: remove local ai (#5837)
* chore: remove local ai

* chore: flutter analyze
2024-07-30 19:44:08 +08:00
nathan
d52a04a9e3 chore: update chat input hint 2024-07-30 17:40:58 +08:00
Lucas.Xu
a14ced458c
chore: bump version 0.6.6 (#5833) 2024-07-30 17:37:28 +08:00
Nathan.fooo
5250a151c8
feat: Chat app download (#5835)
* chore: downlaod chat app

* chore: download app

* chore: disable local ai in billing

* chore: remove watcher

* chore: flutter analyzer
2024-07-30 17:32:30 +08:00
Mathias Mogensen
d5a5a64fcf
fix: photo gallery improvements + launch review fixes (#5830)
* fix: photo gallery improvements

* fix: improve when to show billing/plan pages

* feat: add grid photo gallery layout

* fix: close inline actions menu
2024-07-30 15:28:40 +08:00
Lucas.Xu
175c90e379
chore: update collab 93ec9a38722d5ed0628e6d03a161e2de172967f3 (#5832) 2024-07-30 15:28:21 +08:00
Lucas.Xu
115ae27ab1
fix: missing system font display (#5825)
* fix: missing system font display

* fix: integration test

* fix: cloud integration test
2024-07-29 14:30:11 +08:00
Chen ☁️☁️
e908892b4a
chore: update simplified Chinese translate (#5798) 2024-07-29 10:00:05 +08:00
hasanbeder
948d61a9e0
chore: update tr-TR.json (#5822)
chore: Update Turkish translations
2024-07-29 09:59:25 +08:00
Lucas.Xu
b5c4786896
fix: 0.6.6 launch review issues (#5824)
* fix: only rebuild the sidebar header when needed

* fix: skip the null or empty awareness data

* fix: ai writer setState error

* chore: add arrow down icon when hovering on workspace button

* chore: update workspace button tooltip sytle

* chore: only show collapse all subpages button when then page is expanded

* chore: update pin color

* chore: fix integration test

* Revert "chore: fix integration test"

This reverts commit e02b237423141a3c1f6c41c96a9398a1ebb846cd.
2024-07-29 09:57:58 +08:00
Nathan.fooo
d24f1c566a
fix: chat bugs (#5823)
* chore: implement slash menu

* chore: show popup

* chore: single column

* chore: update appflowy editor

* chore: fix warns
2024-07-27 23:47:08 +08:00
Lucas.Xu
ddf68b010d
chore: improve UI design on mobile (#5816) 2024-07-27 21:05:51 +08:00
Nathan.fooo
22b108df70
chore: enable billing (#5818) 2024-07-26 12:03:29 +08:00
Nathan.fooo
aff9a61919
chore: notify in release build (#5817) 2024-07-26 10:28:18 +08:00
Lucas.Xu
043cd3f3bb
fix: chat messaage color doesn't update when switching theme (#5815) 2024-07-26 10:08:00 +08:00
Lucas.Xu
29fb4af40a
chore: improve UI design on Desktop (#5792)
* fix: only show collapse page button when the children of the page is not emtpy

* chore: set minimum sidebar width to 268

* chore: replace space lock icon and pin & unpin icon

* chore: change divider color

* chore: update divider color

* chore: improve create space color

* feat: highlight delete button when hovering

* chore: update translations

* fix: icon align issue

* feat: highlight sidebar resizer when hovering

* feat: add border to popover

* feat: optimize scroll bar

* feat: improve scrollbar hover color

* feat: support creating a new page via cmd+n

* chore: improve scrollbar color

* feat: improve tooltip style

* chore: fix unit test

* chore: bump version 0.6.6
2024-07-26 09:49:13 +08:00
Nathan.fooo
a2e211555e
chore: chat with pdf ui (#5811)
* chore: chat with pdf ui

* chore: only enable local ai on macos

* chore: add todo

* chore: adjust UI

* chore: clippy
2024-07-26 07:58:54 +08:00
Mathias Mogensen
d1af172fb7
feat: drop images into editor (#5813) 2024-07-26 00:08:55 +02:00
Mathias Mogensen
23b6f94e82
feat: photo gallery block + image improvements (#5803)
* feat: support multiple images in image block

* feat: support drop files on image placeholder

* fix: overflow in image placeholder

* chore: clean code

* feat: refactor to multi image block

* feat: drop image on gallery to add

* feat: add delete image inside interactive viewer

* fix: some mobile improvements

* fix: web ci

* test: fix tests after dialog changes

* test: add basic multi image block test

* test: add to test runner

* test: open interactive viewer

* fix: add delete index to callback

* test: add navigation next/previous

* ci: fix

* ci: fix

* ci: fix

* test: add network image + deletion tests

* fix: remove duplicates after merge

* test: add multi image insertion test

* ci: try

* ci: try

---------

Co-authored-by: nathan <nathan@appflowy.io>
2024-07-25 14:47:08 +02:00
Nathan.fooo
82fffba45a
chore: document local ai (#5810)
* chore: document local ai

* chore: update ui

* chore: clippy
2024-07-25 19:41:16 +08:00
charles
0373088fb8
fix: defaultWindowSize is out of scope (#5795) (#5796) 2024-07-25 13:53:46 +08:00
Lucas.Xu
0da0f320b7
fix: the action menu is blocked by the navigation bar on Android (#5806) 2024-07-25 13:52:20 +08:00
Lucas.Xu
4abdcd9478
fix: integration test (reset to default location) (#5804)
* fix: integration test (reset to default location)

* fix: unit test (appflowy cloud anon user and then sign in)

* Revert "fix: unit test (appflowy cloud anon user and then sign in)"

This reverts commit 2635aa42f013318a4395216b126b2e3d9336ab0b.

* fix: unit test (appflowy cloud anon user and then sign in)
2024-07-25 12:02:18 +08:00
Nathan.fooo
1e54c2dc7c
chore: only run appflowy cloud if it's not exist (#5799)
* chore: only run appflowy cloud if it's not exist

* chore: update

* chore: log error

* chore: fix sign out test

* chore: update test
2024-07-25 07:59:03 +08:00
Lucas.Xu
01747f13e8
chore: bump version 0.6.5 (#5800) 2024-07-24 14:53:56 +08:00
Nathan.fooo
4a5eda6eeb
chore: enable billing (#5779)
* chore: enable billing

* chore: adjust bright mode UI

* chore: show corresponding error in sidebar

* chore: dismiss dialog in ai writter when hit ai response

* fix: improvements from test session

* chore: ai error message for database

* chore: different prompt for workspace owner

* feat: cancel plan survey

* chore: show ai repsonse limit on chat

* fix: sidebar toast after merge

* chore: remove unused debug print

* fix: popover close on action

* fix: minor copy changes

* chore: disable billing

* chore: disbale billing

---------

Co-authored-by: Mathias Mogensen <mathiasrieckm@gmail.com>
2024-07-24 14:23:09 +08:00
Lucas.Xu
b5d799655a
fix: delete reference databases in toggle list (#5782) 2024-07-24 11:24:33 +08:00
Nathan.fooo
43b250ec28
CI: use self-hosted runner if developer can create a branch in the up-stream repo (#5791)
* chore: use self hosted macos runner
2024-07-24 10:55:58 +08:00
Khor Shu Heng
20e82880ff
chore: remove unused step in rust CI, allow cache on failure, show container logs (#5770) 2024-07-24 10:03:46 +08:00
Khor Shu Heng
661a0879c6
fix: Remove empty edit from read me json (#5769)
* fix: Remove empty edit from read me json

* fix: remove delta field entirely
2024-07-24 09:45:41 +08:00
Kilu.He
2fb18dd051
fix: to 16px (#5790)
* fix: to 16px

* fix: to 16px

* fix: to 14px
2024-07-23 17:48:51 +08:00
Kilu.He
6334255e15
fix: some style issues (#5783) 2024-07-23 10:21:50 +08:00
Lucas.Xu
f66821715f
fix: integration test failed (grid row detail page: hide and show hidden fields) (#5781) 2024-07-22 16:51:58 +08:00
Mathias Mogensen
620e027c3e
feat: ai billing (#5741)
* feat: start on AI plan+billing UI

* chore: enable plan and billing

* feat: cache workspace subscription + minor fixes (#5705)

* feat: update api from billing

* feat: add api for workspace subscription info (#5717)

* feat: refactor and start integrating AI plans

* feat: refine UI and add business logic for AI

* feat: complete UIUX for AI and limits

* chore: remove resolved todo

* chore: localize remove addon dialog

* chore: fix spacing issue for usage

* fix: interpret subscription + usage on action

* chore: update api for billing (#5735)

* chore: update revisions

* fix: remove subscription cache

* fix: copy improvements + use consistent dialog

* chore: update to the latest client api

* feat: support updating billing period

* Feat/ai billing cancel reason (#5752)

* chore: add cancellation reason field

* fix: ci add one retry for concurrent sign up

* chore: merge with main

* chore: half merge

* chore: fix conflict

* chore: observer error

* chore: remove unneeded protobuf and remove unwrap

* feat: added subscription plan details

* chore: check error code and update sidebar toast

* chore: periodically check billing state

* chore: editor ai error

* chore: return file upload error

* chore: fmt

* chore: clippy

* chore: disable upload image when exceed storage limitation

* chore: remove todo

* chore: remove openai i18n

* chore: update log

* chore: update client-api to fix stream error

* chore: clippy

* chore: fix language file

* chore: disable billing UI

---------

Co-authored-by: Zack Fu Zi Xiang <speed2exe@live.com.sg>
Co-authored-by: nathan <nathan@appflowy.io>
2024-07-22 15:43:48 +08:00
Kilu.He
864768b3ba
fix: can not display rows when rows overthan 1000 (#5777) 2024-07-22 15:38:13 +08:00
Kilu.He
a8b4f22703
feat: support publish database on web (#5748)
* fix: view name

* fix: hidden login on web

* fix: database update bugs

* feat: support render database on web

* fix: loading

* fix: calendar width on mobile

* fix: calendar boder color

* fix: replace some icons

* fix: deal with visible view ids

* fix: filter error child

* fix: hide filters and sorts

* fix: the style of relation

* fix: throw error when apply fail

* fix: upgrade yjs

* fix: eslint errors

* fix: support group by checkbox

* fix: add shortcut to clear data

* fix: relation

* fix: relation

* fix: relation

* fix: relation

* fix: view meta

* fix: view meta

* fix: view meta

* fix: empty database block

* fix: 0716 bugs

* fix: add button to url cell

* fix: jest test

* fix: unit tests

* fix: lint

* fix: reduce database space

* fix: add after payment page

* fix: add spacing

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2024-07-22 13:37:27 +08:00
Lucas.Xu
432db0f6d5
feat: publish databse to Web (#5709)
* chore: refactor the publish code

* feat: integrate publish into database page

* feat: add publish database structure

* feat: add database row collab

* feat: publish the database row collabs

* chore: update collab

* chore: improve question bubble

* feat: publish the database relations

* fix: rust ci

* feat: select grid view to publish

* feat: unable to deselect the primary database

* feat: optimize the read recent views speed (#5726)

* feat: optimize the read recent views speed

* fix: order of recent views should be from the latest to the oldest

* chore: update translations

* fix: replace the unable to be selected icon

* feat: remove left padding of inline database

* fix: code review

* chore: remove publish api err log

* chore: read the database collab and document collab from disk instead of memory

* chore: code cleanup

* chore: revert beta.appflowy.com

* chore: code cleanup

* test: add database encode test

* test: add publish database test

* chore: refresh sidebar layout

* chore: update comments
2024-07-22 13:35:42 +08:00
Abhijeet Singh
e8e4162a5c
chore: update Spanish translations (#5742)
Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2024-07-22 10:15:50 +08:00
sayishi
241234726f
chore: update Chinese translations (#5727)
* chore: update translations with Fink 🐦

* chore: revert cargo.lock changes

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2024-07-22 10:07:50 +08:00
Khor Shu Heng
e426970eed
fix: Add retry for admin client sign in for test (#5767) 2024-07-22 10:02:01 +08:00
Yaron Shahrabani
d5544af6c5
chore: update Hebrew translation (#5738)
* Added Hebrew initial infra.

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* Hebrew translation.

* Hebrew update.

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2024-07-22 10:00:24 +08:00
Stefan Weiberg
9361afd573
chore: update German translations (#5722) 2024-07-19 16:40:03 +08:00
Artjom Jemeljanov
dc93a336d6
chore: update Russian translations (#5730)
* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* chore: update translations with Fink 🐦

* Update en.json

* Update ru-RU.json

* Update ru-RU.json

* chore: update translations with Fink 🐦

* Update settings.json

* Update settings.json
2024-07-19 16:26:00 +08:00
Nathan.fooo
4e5482488f
chore: update build config (#5759)
* chore: update build config

* chore: fix ci

* chore: update client-api

* chore: fix compile
2024-07-19 12:23:51 +08:00
Nathan.fooo
5bbf174ffd
chore: enable local ai and local ai chat (#5755)
* chore: enable local ai and local ai chat

* chore: config for chat with file

* chore: flutter anaylzer
2024-07-18 20:54:35 +08:00
Lucas.Xu
f36e3ae378
chore: bump version 0.6.4 (#5744) 2024-07-16 14:51:00 +08:00
Mathias Mogensen
dc6349b4b1
fix: improve color selector (#5743)
Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2024-07-16 14:50:50 +08:00
Lucas.Xu
44d8def3ce
fix: reset space relationship when clearing cache (#5737)
* fix: space's child views didn't update when moving a page into it

* chore: remove check logic

* feat: integrate fix space into clear cache

* fix: translation missing value

* chore: update logger version
2024-07-16 13:23:07 +08:00
Nathan.fooo
c6ad57f11d
chore: show plugin state (#5740)
* chore: show plugin state

* chore: flutter analyzer

* chore: update
2024-07-15 22:45:53 +08:00
Nathan.fooo
ff23165d3e
chore: download llm files (#5723)
* chore: download file

* chore: config download ui

* chore: update zip

* chore: config download ui

* chore: unzip file

* chore: unzip file

* chore: rename

* chore: disable local ai

* chore: fmt

* chore: fix warning

* chore: update

* chore: fix clippy
2024-07-15 15:23:23 +08:00
Lucas.Xu
253e7597c4
feat: optimize the read recent views speed (#5726)
* feat: optimize the read recent views speed

* fix: order of recent views should be from the latest to the oldest
2024-07-15 14:23:20 +08:00
Zack
d2e3fdfefd
chore: fix compile (#5733) 2024-07-15 09:01:34 +08:00
Lucas.Xu
a2336f5bf7
chore: improve popup menu color (#5725)
* chore: improve popup menu color

* fix: 100% text wrap into a new line in checklist cell
2024-07-12 15:23:22 +08:00
Lucas.Xu
8833df1740
fix: ai chat result contains html escape text (#5721)
* fix: ai chat result contains html escape text

* feat: support copy the code block from ai chat page

* feat: support copy code block from ai chat page

* fix: add hover effect to color picker button

* chore: increase minimum window size
2024-07-11 21:42:02 +08:00
Lucas.Xu
1524b10a8a
chore: bump version 0.6.4 (#5719)
* chore: upgrade android target sdk to 34

* chore: downgrade macos builder version to support x86_64

* chore: bump version 0.6.4
2024-07-11 13:46:28 +08:00
Kilu.He
fe0fa9b530
feat: support sign-in and sign-up on Web (#5712) 2024-07-11 12:55:22 +08:00
Lucas.Xu
80afcf44c0
fix: syntax error when generating translation files (#5718) 2024-07-11 11:56:00 +08:00
Gustavo Moreno
e500c89978
feat: choose cursor/selection color from palette or color picker #5041 (#5677)
* chore: show ai service error (#5675)

* feat: Implement color picker dialog for user color selection (#5041)

- Added `flex_color_picker` dependency.
- Implemented a new color picker dialog with the following features:
  - Display Material predefined colors.
  - Include primary, accent, and shade colors, as well as a color wheel for selection.
  - Add a graphical opacity selector.
  - Enhanced the previous dialog to include an icon in a text field for opening the palette.
- Added `suffixIcon` parameter to the `_ColorSettingTextField` class, making it reactive to tap gestures.
- Utilized `ColorExtension` on the `Color` class to avoid namespace conflicts when converting colors to hexadecimal strings.
- Added translation strings to english

This commit addresses issue #5041

* feat: Implement color picker dialog for user color selection (#5041)

- Added `flex_color_picker` dependency.
- Implemented a new color picker dialog with the following features:
      - Display Material predefined colors.
      - Include primary, accent, and shade colors, as well as a color wheel for selection.
      - Add a graphical opacity selector.
      - Enhanced the previous dialog to include an icon in a text field for opening the palette.
- Added `suffixIcon` parameter to the `_ColorSettingTextField` class, making it reactive to tap gestures.
- Utilized `ColorExtension` on the `Color` class to avoid namespace conflicts when converting colors to hexadecimal strings.
- Added translation strings to english

This commit addresses issue #5041

* chore: refactor code

---------

Co-authored-by: Nathan.fooo <86001920+appflowy@users.noreply.github.com>
Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
2024-07-10 17:49:59 +08:00
Lucas.Xu
079b9888a8
feat: render ai text message with appflowy_editor (#5682)
* feat: render ai text message with appflowy_editor

* chore: update appflowy_editor

* fix: integration test

* feat: support paste inAppJson format

* chore: update appflowy_editor
2024-07-10 17:17:07 +08:00
Lucas.Xu
0fe383e538
feat: enable debug logs in internal build (#5713) 2024-07-10 13:55:40 +08:00
Bartosz Sypytkowski
c006e29afc
chore: update yrs and appflowy collab dependencies (#5707)
* chore: update yrs and appflowy collab dependencies

* chore: fix clippy errors

* chore: update yrs and collab dependencies in appflowy tauri

* chore: update yrs and collab dependencies in appflowy web app
2024-07-10 10:10:21 +08:00
Lucas.Xu
655a2b7093
chore: bump version 0.6.3 (#5701) 2024-07-08 13:46:27 +08:00
Kilu.He
23c67bcdba
feat: support publish document (#5576)
* feat: support a event for getting encoded collab of document

* feat: support publish view and unpublish views

* feat: publish page to the web

* chore: refacotor share bloc

* feat: call the publish event

* feat: support publish view and unpublish views

* feat: integrate publish api

* feat: integrate unpublish api

* feat: fetch the publish info to show the publish status

* feat: support publish interfaces

* fix: lint error

* fix: modified web server

* fix: some style

* fix: some style

* fix: some style

* fix: some style

* fix: some style

* fix: some style

* fix: some style

* fix: some style

* fix: some style

* fix: update codes

* fix: update codes

* fix: update codes

* fix: update codes

* fix: update codes

* chore: refactor publish bloc

* fix: some style

* fix: some style

* fix: some style

* fix: some style

* fix: some style

* fix: some style

* fix: the name is too long to publish

* chore: change color

* fix: some style

* fix: some style

* feat: refacotor share menu UI

* fix: some style

* fix: lint

* fix: some style

* feat: refacotor export-as

* fix: some style

* chore: refactor share menu colors

* fix: rust ci

* fix: some style

* fix: some style

* fix: deploy

* fix: deploy

* fix: deploy

* fix: deploy

* fix: deploy

* fix: deploy

* fix: deploy

* fix: deploy

* fix: deploy

* fix: deploy

* fix: deploy

* fix: deploy

* fix: bugs

* fix: bugs

* fix: bugs

* fix: bugs

* fix: bugs

* fix: bugs

* fix: bugs

* fix: bugs

* fix: bugs

* fix: bugs

* fix: bugs

* fix: bugs

* fix: bugs

* fix: bugs

* fix: bugs

* fix: bugs

* fix: rerelease

* fix: deploy

* fix: deploy

* fix: deploy

* fix: deploy

* fix: deploy

* fix: deploy

* fix: deploy

* fix: og image

* fix: support copy button

* fix: support copy button

* fix: support copy button

* chore: add a params

* feat: use default publish name

* chore: update copy

* feat: show a confirm deletion dialog if the deleted page contains published page

* feat: add copy toast in publish tab

* fix: to 404

fix: to 404

fix: to 404

fix: the error to 404

* feat: unpublish the page auto when moving it to another space

* feat: improve confirm deletion dialog

* feat: show unpublish error

* chore: use beta.appflowy.com

* feat: disable publish in non-apppflowy-cloud user mode

* fix: modified bullted icon style

* fix: the dark mode color

* fix: save the dark mode in local storage

* fix: text color

* chore: make bash script more portable (#5679)

* fix: title longer

* chore: move the files and modified the en

* chore: update deploy.sh

* chore: modified Dockerfile

* chore: modified server.cjs to server.js

* chore: modifed server.js to server.ts

* chore: replace publish url

* chore: remove todo list hover

* chore: show confirm dialog before deleting page

* fix: unpublish the pages before deleting

* fix: table cell bg color

* fix: callout icon

* fix: list number

* fix: emoji

* fix: number icon

* fix: callout icon position

* fix: add margin bottom

* fix: code block

* fix: support scroll for breadcrumbs

* fix: the breadcrumb doesn't update after moving page

* fix: 0705 issues

* fix: update publish status afer deleting page

* chore: add hover effect for visit site button

* fix: remove puiblish url text field enable border color

* chore: update delete page copy

* chore: enable debug category

* fix: only render sidebar if the spaces are ready

* fix: the breadcrumb doesn't update after moving page

* fix: auto code

* fix: add emoji

* fix: add emoji

* fix: favicon

* fix: cypress test

* fix: remove deploy ci

* fix: default url

* chore: revert launch.json

* fix: docker ci

* fix: change favicon

* fix: flutter integration test

* feat: add hover effect to share menu

* chore: add a checkmark if the page has been published

* chore: revert space deletion

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
Co-authored-by: Zack <speed2exe@live.com.sg>
2024-07-08 13:45:57 +08:00
Nathan.fooo
521fffd97c
refactor: extract chat plugin to new repo (#5699)
* chore: update ui

* chore: update

* chore: update

* chore: separate to new crate

* chore: update commit id

* chore: fix compile

* chore: bump version
2024-07-08 13:19:13 +08:00
Lucas.Xu
2ecc2a67a9
fix: take the max value of the keyboard height and the view insets bottom to make the toolbar visible (#5700)
* fix: android toolbar will be blocked if the scroll bar is hidden

* fix: last opened view is synced in same workspace

* fix: fallback to space if the section sidebar contains space

* chore: revert last opened view code

* fix: integration tests
2024-07-08 13:15:42 +08:00
Lucas.Xu
2d11215bb2
chore: use latest macos runner to build the release package (#5686) 2024-07-08 09:43:23 +08:00
2611 changed files with 81986 additions and 29919 deletions

View File

@ -1,72 +0,0 @@
name: Deploy Web (Test)
on:
push:
branches:
- build/test
env:
NODE_VERSION: "18.16.0"
PNPM_VERSION: "8.5.0"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
deploy:
runs-on: ubuntu-latest
env:
SSH_PRIVATE_KEY: ${{ secrets.WEB_TEST_SSH_PRIVATE_KEY }}
REMOTE_HOST: ${{ secrets.WEB_TEST_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.WEB_TEST_REMOTE_USER }}
SSL_CERTIFICATE: ${{ secrets.WEB_TEST_SSL_CERTIFICATE }}
SSL_CERTIFICATE_KEY: ${{ secrets.WEB_TEST_SSL_CERTIFICATE_KEY }}
ENV_FILE: test.env
steps:
- uses: actions/checkout@v4
- name: setup node
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
- name: setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Node_modules cache
uses: actions/cache@v2
with:
path: frontend/appflowy_web_app/node_modules
key: node-modules-${{ runner.os }}
- name: install frontend dependencies
working-directory: frontend/appflowy_web_app
run: |
pnpm install
- name: copy env file
working-directory: frontend/appflowy_web_app
run: |
cp ${{ env.ENV_FILE }} .env
- name: test and lint
working-directory: frontend/appflowy_web_app
run: |
pnpm run lint
- name: build
working-directory: frontend/appflowy_web_app
run: |
pnpm run build
- name: generate SSL certificate
run: |
echo "${{ env.SSL_CERTIFICATE }}" > nginx-signed.crt
echo "${{ env.SSL_CERTIFICATE_KEY }}" > nginx-signed.key
- name: Deploy to EC2
uses: easingthemes/ssh-deploy@main
with:
SSH_PRIVATE_KEY: ${{ env.SSH_PRIVATE_KEY }}
ARGS: "-rlgoDzvc -i"
SOURCE: "frontend/appflowy_web_app/dist frontend/appflowy_web_app/server.cjs frontend/appflowy_web_app/start.sh frontend/appflowy_web_app/Dockerfile frontend/appflowy_web_app/nginx.conf frontend/appflowy_web_app/.env nginx-signed.crt nginx-signed.key"
REMOTE_HOST: ${{ env.REMOTE_HOST }}
REMOTE_USER: ${{ env.REMOTE_USER }}
EXCLUDE: "frontend/appflowy_web_app/dist/, frontend/appflowy_web_app/node_modules/"
SCRIPT_AFTER: |
docker build -t appflowy-web-app .
docker rm -f appflowy-web-app || true
docker run -d -p 80:80 -p 443:443 --name appflowy-web-app appflowy-web-app

View File

@ -13,7 +13,7 @@ on:
- release/*
paths:
- frontend/**
types: [opened, synchronize, reopened, unlocked, ready_for_review]
types: [ opened, synchronize, reopened, unlocked, ready_for_review ]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}

View File

@ -248,10 +248,13 @@ jobs:
env:
BACKEND_VERSION: 0.3.24-amd64
run: |
docker compose down -v --remove-orphans
docker compose pull
docker compose up -d
sleep 10
if [ "$(docker ps --filter name=appflowy-cloud -q)" == "" ]; then
docker compose pull
docker compose up -d
sleep 10
else
echo "Docker container 'appflowy-cloud' is already running."
fi
- name: Checkout source code
uses: actions/checkout@v4

View File

@ -28,20 +28,35 @@ concurrency:
cancel-in-progress: true
jobs:
build:
if: github.event.pull_request.draft != true
strategy:
fail-fast: true
matrix:
os: [ macos-14 ]
runs-on: ${{ matrix.os }}
build-self-hosted:
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: self-hosted
steps:
- name: Checkout source code
uses: actions/checkout@v2
- name: Build AppFlowy
working-directory: frontend
run: |
cargo make --profile development-ios-arm64-sim appflowy-core-dev-ios
cargo make --profile development-ios-arm64-sim code_generation
- uses: futureware-tech/simulator-action@v3
id: simulator-action
with:
model: 'iPhone 15'
shutdown_after_job: false
build-macos:
if: github.event.pull_request.head.repo.full_name != github.repository
runs-on: macos-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 }}
@ -49,8 +64,7 @@ jobs:
override: true
profile: minimal
- name: Install flutter
id: flutter
- name: Install Flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
@ -59,7 +73,7 @@ jobs:
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ matrix.os }}
prefix-key: macos-latest
workspaces: |
frontend/rust-lib

View File

@ -135,7 +135,7 @@ jobs:
fail-fast: false
matrix:
job:
- { target: x86_64-apple-darwin, os: macos-11, extra-build-args: "" }
- { target: x86_64-apple-darwin, os: macos-12, extra-build-args: "" }
steps:
- name: Checkout source code
uses: actions/checkout@v4
@ -233,7 +233,7 @@ jobs:
job:
- {
targets: "aarch64-apple-darwin,x86_64-apple-darwin",
os: macos-11,
os: macos-latest,
extra-build-args: "",
}
steps:
@ -479,6 +479,24 @@ jobs:
cache-from: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/af_build_cache:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/af_build_cache:buildcache,mode=max
notify-failure:
runs-on: ubuntu-latest
needs:
- build-for-macOS-x86_64
- build-for-windows
- build-for-linux
if: failure()
steps:
- uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: |
🔴🔴🔴Workflow ${{ github.workflow }} in repository ${{ github.repository }} was failed 🔴🔴🔴.
fields: repo,message,author,eventName,ref,workflow
env:
SLACK_WEBHOOK_URL: ${{ secrets.RELEASE_SLACK_WEBHOOK }}
if: always()
notify-discord:
runs-on: ubuntu-latest
needs:

View File

@ -8,6 +8,7 @@ on:
- "release/*"
paths:
- "frontend/rust-lib/**"
- ".github/workflows/rust_ci.yaml"
pull_request:
branches:
@ -22,70 +23,25 @@ env:
RUST_TOOLCHAIN: "1.77.2"
jobs:
test-on-ubuntu:
runs-on: ubuntu-latest
self-hosted-job:
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: self-hosted
steps:
# - name: Maximize build space
# uses: easimon/maximize-build-space@master
# with:
# root-reserve-mb: 2048
# swap-size-mb: 1024
# remove-dotnet: 'true'
# the following step is required to avoid running out of space
- name: Maximize build space
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf "/usr/local/share/boost"
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
sudo docker image prune --all --force
- name: Checkout source code
uses: actions/checkout@v4
- 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 prerequisites
working-directory: frontend
run: |
cargo install --force cargo-make
cargo install --force duckscript_cli
- uses: Swatinem/rust-cache@v2
with:
prefix-key: "ubuntu-latest"
workspaces: |
frontend/rust-lib
- name: Checkout appflowy cloud code
- name: Checkout Appflowy Cloud
uses: actions/checkout@v4
with:
repository: AppFlowy-IO/AppFlowy-Cloud
path: AppFlowy-Cloud
- name: Prepare appflowy cloud env
- name: Prepare Appflowy Cloud env
working-directory: AppFlowy-Cloud
run: |
# log level
cp deploy.env .env
sed -i 's|RUST_LOG=.*|RUST_LOG=trace|' .env
sed -i 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
- name: Run Docker-Compose
working-directory: AppFlowy-Cloud
env:
BACKEND_VERSION: 0.3.24-amd64
run: |
docker pull appflowyinc/appflowy_cloud:latest
docker compose up -d
sed -i '' 's|RUST_LOG=.*|RUST_LOG=trace|' .env
sed -i '' 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
- name: Run rust-lib tests
working-directory: frontend/rust-lib
@ -106,6 +62,84 @@ jobs:
run: cargo clippy --all-targets -- -D warnings
working-directory: frontend/rust-lib
ubuntu-job:
if: github.event.pull_request.head.repo.full_name != github.repository
runs-on: ubuntu-latest
steps:
- name: Maximize build space
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf "/usr/local/share/boost"
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
sudo docker image prune --all --force
- name: Checkout source code
uses: actions/checkout@v4
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
override: true
components: rustfmt, clippy
profile: minimal
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ runner.os }}
cache-on-failure: true
workspaces: |
frontend/rust-lib
- name: Checkout appflowy cloud code
uses: actions/checkout@v4
with:
repository: AppFlowy-IO/AppFlowy-Cloud
path: AppFlowy-Cloud
- name: Prepare appflowy cloud env
working-directory: AppFlowy-Cloud
run: |
cp deploy.env .env
sed -i 's|RUST_LOG=.*|RUST_LOG=trace|' .env
sed -i 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
- name: Run Docker-Compose
working-directory: AppFlowy-Cloud
run: |
if [ "$(docker ps --filter name=appflowy-cloud -q)" == "" ]; then
docker compose pull
docker compose up -d
sleep 10
else
echo "Docker container 'appflowy-cloud' is already running."
fi
- name: Run rust-lib tests
working-directory: frontend/rust-lib
env:
RUST_LOG: info
RUST_BACKTRACE: 1
af_cloud_test_base_url: http://localhost
af_cloud_test_ws_url: ws://localhost/ws/v1
af_cloud_test_gotrue_url: http://localhost/gotrue
run: |
DISABLE_CI_TEST_LOG="true" cargo test --no-default-features --features="dart"
- name: rustfmt rust-lib
run: cargo fmt --all -- --check
working-directory: frontend/rust-lib/
- name: clippy rust-lib
run: cargo clippy --all-targets -- -D warnings
working-directory: frontend/rust-lib
- name: "Debug: show Appflowy-Cloud container logs"
if: failure()
working-directory: AppFlowy-Cloud
run: |
docker compose logs appflowy_cloud
- name: Clean up Docker images
run: |
docker image prune -af

View File

@ -1,4 +1,5 @@
name: Tauri-CI
on:
pull_request:
paths:
@ -11,28 +12,47 @@ env:
NODE_VERSION: "18.16.0"
PNPM_VERSION: "8.5.0"
RUST_TOOLCHAIN: "1.77.2"
CARGO_MAKE_VERSION: "0.36.6"
CI: true
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
tauri-build:
if: github.event.pull_request.draft != true
strategy:
fail-fast: false
matrix:
platform: [ ubuntu-20.04 ]
# tauri-build-self-hosted:
# if: github.event.pull_request.head.repo.full_name == github.repository
# runs-on: self-hosted
#
# steps:
# - uses: actions/checkout@v4
# - name: install frontend dependencies
# working-directory: frontend/appflowy_web_app
# run: |
# mkdir dist
# pnpm install
# cd src-tauri && cargo build
#
# - name: test and lint
# working-directory: frontend/appflowy_web_app
# run: |
# pnpm run lint:tauri
#
# - uses: tauri-apps/tauri-action@v0
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# with:
# tauriScript: pnpm tauri
# projectPath: frontend/appflowy_web_app
# args: "--debug"
runs-on: ${{ matrix.platform }}
tauri-build-ubuntu:
#if: github.event.pull_request.head.repo.full_name != github.repository
runs-on: ubuntu-20.04
env:
CI: true
steps:
- uses: actions/checkout@v4
- name: Maximize build space (ubuntu only)
if: matrix.platform == 'ubuntu-20.04'
- name: Maximize build space
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
@ -61,36 +81,27 @@ jobs:
override: true
profile: minimal
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: "./frontend/appflowy_web_app/src-tauri -> target"
- name: Node_modules cache
uses: actions/cache@v2
with:
path: frontend/appflowy_web_app/node_modules
key: node-modules-${{ runner.os }}
- name: install dependencies (windows only)
if: matrix.platform == 'windows-latest'
working-directory: frontend
run: |
cargo install --force duckscript_cli
vcpkg integrate install
- name: install dependencies (ubuntu only)
if: matrix.platform == 'ubuntu-20.04'
- name: install dependencies
working-directory: frontend
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
- name: install cargo-make
- uses: taiki-e/install-action@v2
with:
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}
- name: install tauri deps tools
working-directory: frontend
run: |
cargo install --force cargo-make
cargo make appflowy-tauri-deps-tools
shell: bash
- name: install frontend dependencies
working-directory: frontend/appflowy_web_app

View File

@ -1,4 +1,52 @@
# Release Notes
## Version 0.6.8 - 22/08/2024
### New Features
- Optimized date picker and mention block.
- Added the ability to open database row on mobile.
- Added the ability to invite members to workspace on mobile.
- Added support for Monochrome theme on Android.
- Added AI Bubble button on homepage on mobile.
- Settings, trash, members and help & support have been moved into the settings pop up menu.
### Bug Fixes
- Removed Wayland header from AppImage build
- Fixed the issue where pasting web image on mobile failed.
## Version 0.6.7 - 13/08/2024
### New Features
- Redesigned the icon picker design on Desktop.
- Redesigned the notification page on Mobile.
### Bug Fixes
- Enhance the toolbar tooltip functionality on Desktop.
- Enhance the slash menu user experience on Desktop.
- Fixed the issue where list style overrides occurred during text pasting.
- Fixed the issue where linking multiple databases in the same document could cause random loss of focus.
## Version 0.6.6 - 30/07/2024
### New Features
- Upgrade your workspace to a premium plan to unlock more features and storage.
- Image galleries and drag-and-drop image support in documents.
### Bug Fixes
- Fix minor UI issues on Desktop and Mobile.
## Version 0.6.5 - 24/07/2024
### New Features
- Publish a Database to the Web
## Version 0.6.4 - 16/07/2024
### New Features
- Enhanced the message style on the AI chat page.
- Added the ability to choose cursor color and selection color from a palette in settings page.
### Bug Fixes
- Optimized the performance for loading recent pages.
- Fixed an issue where the cursor would jump randomly when typing in the document title on mobile.
## Version 0.6.3 - 08/07/2024
### New Features
- Publish a Document to the Web
## Version 0.6.2 - 01/07/2024
### New Features
- Added support for duplicating spaces.

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

View File

@ -13,7 +13,6 @@
"type": "dart",
"env": {
"RUST_LOG": "debug",
"RUST_BACKTRACE": "1"
},
// uncomment the following line to testing performance.
// "flutterMode": "profile",
@ -138,4 +137,4 @@
"cwd": "${workspaceRoot}/appflowy_tauri/"
},
]
}
}

View File

@ -26,7 +26,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
CARGO_MAKE_CRATE_NAME = "dart-ffi"
LIB_NAME = "dart_ffi"
APPFLOWY_VERSION = "0.6.2"
APPFLOWY_VERSION = "0.6.8"
FLUTTER_DESKTOP_FEATURES = "dart"
PRODUCT_NAME = "AppFlowy"
MACOSX_DEPLOYMENT_TARGET = "11.0"

View File

@ -53,7 +53,7 @@ android {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "io.appflowy.appflowy"
minSdkVersion 24
targetSdkVersion 33
targetSdkVersion 34
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
multiDexEnabled true

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/black" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background" />
<foreground
android:drawable="@mipmap/ic_launcher_foreground" />
<monochrome
android:drawable="@mipmap/ic_launcher_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>

File diff suppressed because one or more lines are too long

View File

View File

@ -0,0 +1,12 @@
# dart_dependency_validator.yaml
allow_pins: true
include:
- "lib/**"
exclude:
- "packages/**"
ignore:
- analyzer

View File

@ -42,7 +42,7 @@ void main() {
await tester.tapAnonymousSignInButton();
await tester.expectToSeeHomePageWithGetStartedPage();
// reanme the name of the anon user
// rename the name of the anon user
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.account);
await tester.pumpAndSettle();

View File

@ -47,11 +47,11 @@ void main() {
);
await tester.tapButton(find.byType(SignInOutButton));
tester.expectToSeeText(LocaleKeys.button_confirm.tr());
await tester.tapButtonWithName(LocaleKeys.button_confirm.tr());
tester.expectToSeeText(LocaleKeys.button_ok.tr());
await tester.tapButtonWithName(LocaleKeys.button_ok.tr());
// Go to the sign in page again
await tester.pumpAndSettle(const Duration(seconds: 1));
await tester.pumpAndSettle(const Duration(seconds: 5));
tester.expectToSeeGoogleLoginButton();
});

View File

@ -1,93 +1,93 @@
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/presentation/settings/pages/settings_account_view.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_supabase_cloud.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
// import 'package:appflowy/env/cloud_env.dart';
// import 'package:appflowy/workspace/application/settings/prelude.dart';
// import 'package:appflowy/workspace/presentation/settings/pages/settings_account_view.dart';
// import 'package:appflowy/workspace/presentation/settings/widgets/setting_supabase_cloud.dart';
// import 'package:flutter_test/flutter_test.dart';
// import 'package:integration_test/integration_test.dart';
import '../shared/util.dart';
// import '../shared/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
// void main() {
// IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('supabase auth', () {
testWidgets('sign in with supabase', (tester) async {
await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
await tester.tapGoogleLoginInButton();
await tester.expectToSeeHomePageWithGetStartedPage();
});
// group('supabase auth', () {
// testWidgets('sign in with supabase', (tester) async {
// await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
// await tester.tapGoogleLoginInButton();
// await tester.expectToSeeHomePageWithGetStartedPage();
// });
testWidgets('sign out with supabase', (tester) async {
await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
await tester.tapGoogleLoginInButton();
// testWidgets('sign out with supabase', (tester) async {
// await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
// await tester.tapGoogleLoginInButton();
// Open the setting page and sign out
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.account);
await tester.logout();
// // Open the setting page and sign out
// await tester.openSettings();
// await tester.openSettingsPage(SettingsPage.account);
// await tester.logout();
// Go to the sign in page again
await tester.pumpAndSettle(const Duration(seconds: 1));
tester.expectToSeeGoogleLoginButton();
});
// // Go to the sign in page again
// await tester.pumpAndSettle(const Duration(seconds: 1));
// tester.expectToSeeGoogleLoginButton();
// });
testWidgets('sign in as anonymous', (tester) async {
await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
await tester.tapSignInAsGuest();
// testWidgets('sign in as anonymous', (tester) async {
// await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
// await tester.tapSignInAsGuest();
// should not see the sync setting page when sign in as anonymous
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.account);
// // should not see the sync setting page when sign in as anonymous
// await tester.openSettings();
// await tester.openSettingsPage(SettingsPage.account);
// Scroll to sign-out
await tester.scrollUntilVisible(
find.byType(SignInOutButton),
100,
scrollable: find.findSettingsScrollable(),
);
await tester.tapButton(find.byType(SignInOutButton));
// // Scroll to sign-out
// await tester.scrollUntilVisible(
// find.byType(SignInOutButton),
// 100,
// scrollable: find.findSettingsScrollable(),
// );
// await tester.tapButton(find.byType(SignInOutButton));
tester.expectToSeeGoogleLoginButton();
});
// tester.expectToSeeGoogleLoginButton();
// });
// testWidgets('enable encryption', (tester) async {
// await tester.initializeAppFlowy(cloudType: CloudType.supabase);
// await tester.tapGoogleLoginInButton();
// // testWidgets('enable encryption', (tester) async {
// // await tester.initializeAppFlowy(cloudType: CloudType.supabase);
// // await tester.tapGoogleLoginInButton();
// // Open the setting page and sign out
// await tester.openSettings();
// await tester.openSettingsPage(SettingsPage.cloud);
// // // Open the setting page and sign out
// // await tester.openSettings();
// // await tester.openSettingsPage(SettingsPage.cloud);
// // the switch should be off by default
// tester.assertEnableEncryptSwitchValue(false);
// await tester.toggleEnableEncrypt();
// // // the switch should be off by default
// // tester.assertEnableEncryptSwitchValue(false);
// // await tester.toggleEnableEncrypt();
// // the switch should be on after toggling
// tester.assertEnableEncryptSwitchValue(true);
// // // the switch should be on after toggling
// // tester.assertEnableEncryptSwitchValue(true);
// // the switch can not be toggled back to off
// await tester.toggleEnableEncrypt();
// tester.assertEnableEncryptSwitchValue(true);
// });
// // // the switch can not be toggled back to off
// // await tester.toggleEnableEncrypt();
// // tester.assertEnableEncryptSwitchValue(true);
// // });
testWidgets('enable sync', (tester) async {
await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
await tester.tapGoogleLoginInButton();
// testWidgets('enable sync', (tester) async {
// await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
// await tester.tapGoogleLoginInButton();
// Open the setting page and sign out
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.cloud);
// // Open the setting page and sign out
// await tester.openSettings();
// await tester.openSettingsPage(SettingsPage.cloud);
// the switch should be on by default
tester.assertSupabaseEnableSyncSwitchValue(true);
await tester.toggleEnableSync(SupabaseEnableSync);
// // the switch should be on by default
// tester.assertSupabaseEnableSyncSwitchValue(true);
// await tester.toggleEnableSync(SupabaseEnableSync);
// the switch should be off
tester.assertSupabaseEnableSyncSwitchValue(false);
// // the switch should be off
// tester.assertSupabaseEnableSyncSwitchValue(false);
// the switch should be on after toggling
await tester.toggleEnableSync(SupabaseEnableSync);
tester.assertSupabaseEnableSyncSwitchValue(true);
});
});
}
// // the switch should be on after toggling
// await tester.toggleEnableSync(SupabaseEnableSync);
// tester.assertSupabaseEnableSyncSwitchValue(true);
// });
// });
// }

View File

@ -47,13 +47,12 @@ void main() {
await tester.openSettingsPage(SettingsPage.account);
await tester.enterUserName(name);
await tester.tapEscButton();
await tester.pumpAndSettle(const Duration(seconds: 6));
await tester.logout();
// wait 2 seconds for the sync to finish
await tester.pumpAndSettle(const Duration(seconds: 2));
});
});
testWidgets('get user icon and name from server', (tester) async {
await tester.initializeAppFlowy(
cloudType: AuthenticatorType.appflowyCloudSelfHost,

View File

@ -461,22 +461,22 @@ void main() {
tester.assertChecklistEditorVisible(visible: true);
// create a new task with enter
await tester.createNewChecklistTask(name: "task 0", enter: true);
await tester.createNewChecklistTask(name: "task 1", enter: true);
// assert that the task is displayed
tester.assertChecklistTaskInEditor(
index: 0,
name: "task 0",
name: "task 1",
isChecked: false,
);
// update the task's name
await tester.renameChecklistTask(index: 0, name: "task 1");
await tester.renameChecklistTask(index: 0, name: "task 11");
// assert that the task's name is updated
tester.assertChecklistTaskInEditor(
index: 0,
name: "task 1",
name: "task 11",
isChecked: false,
);

View File

@ -1,10 +1,10 @@
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
@ -25,6 +25,7 @@ void main() {
const lines = 3;
final text = List.generate(lines, (index) => 'line $index').join('\n');
AppFlowyClipboard.mockSetData(AppFlowyClipboardData(text: text));
ClipboardService.mockSetData(ClipboardServiceData(plainText: text));
await insertCodeBlockInDocument(tester);
@ -51,7 +52,9 @@ Future<void> insertCodeBlockInDocument(WidgetTester tester) async {
// open the actions menu and insert the codeBlock
await tester.editor.showSlashMenu();
await tester.editor.tapSlashMenuItemWithName(
LocaleKeys.document_selectionMenu_codeBlock.tr(),
LocaleKeys.document_slashMenu_name_code.tr(),
offset: 150,
);
// wait for the codeBlock to be inserted
await tester.pumpAndSettle();
}

View File

@ -1,11 +1,10 @@
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
@ -166,6 +165,44 @@ void main() {
});
});
testWidgets('paste text on part of bullet list', (tester) async {
const plainText = 'test';
await tester.pasteContent(
plainText: plainText,
beforeTest: (editorState) async {
final transaction = editorState.transaction;
transaction.insertNodes(
[0],
[
Node(
type: BulletedListBlockKeys.type,
attributes: {
'delta': [
{"insert": "bullet list"},
],
},
),
],
);
// Set the selection to the second numbered list node (which has empty delta)
transaction.afterSelection = Selection(
start: Position(path: [0], offset: 7),
end: Position(path: [0], offset: 11),
);
await editorState.apply(transaction);
await tester.pumpAndSettle();
},
(editorState) {
final node = editorState.getNodeAtPath([0]);
expect(node?.delta?.toPlainText(), 'bullet test');
expect(node?.type, BulletedListBlockKeys.type);
},
);
});
testWidgets('paste image(png) from memory', (tester) async {
final image = await rootBundle.load('assets/test/images/sample.png');
final bytes = image.buffer.asUint8List();
@ -246,10 +283,6 @@ void main() {
expect(editorState.document.root.children.length, 2);
final node = editorState.getNodeAtPath([0])!;
expect(node.type, ImageBlockKeys.type);
expect(
node.attributes[ImageBlockKeys.url],
'https://user-images.githubusercontent.com/9403740/262918875-603f4adb-58dd-49b5-8201-341d354935fd.png',
);
},
);
},

View File

@ -12,10 +12,13 @@ import 'document_more_actions_test.dart' as document_more_actions_test;
import 'document_text_direction_test.dart' as document_text_direction_test;
import 'document_with_cover_image_test.dart' as document_with_cover_image_test;
import 'document_with_database_test.dart' as document_with_database_test;
import 'document_with_file_test.dart' as document_with_file_test;
import 'document_with_image_block_test.dart' as document_with_image_block_test;
import 'document_with_inline_math_equation_test.dart'
as document_with_inline_math_equation_test;
import 'document_with_inline_page_test.dart' as document_with_inline_page_test;
import 'document_with_multi_image_block_test.dart'
as document_with_multi_image_block_test;
import 'document_with_outline_block_test.dart' as document_with_outline_block;
import 'document_with_toggle_list_test.dart' as document_with_toggle_list_test;
import 'edit_document_test.dart' as document_edit_test;
@ -38,6 +41,8 @@ void startTesting() {
document_text_direction_test.main();
document_option_action_test.main();
document_with_image_block_test.main();
document_with_multi_image_block_test.main();
document_inline_page_reference_test.main();
document_more_actions_test.main();
document_with_file_test.main();
}

View File

@ -1,4 +1,3 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database/board/presentation/board_page.dart';
import 'package:appflowy/plugins/database/calendar/presentation/calendar_page.dart';
import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart';
@ -7,7 +6,6 @@ import 'package:appflowy/plugins/inline_actions/widgets/inline_actions_handler.d
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/uuid.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
@ -22,7 +20,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
await insertReferenceDatabase(tester, ViewLayoutPB.Grid);
await insertLinkedDatabase(tester, ViewLayoutPB.Grid);
// validate the referenced grid is inserted
expect(
@ -50,7 +48,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
await insertReferenceDatabase(tester, ViewLayoutPB.Board);
await insertLinkedDatabase(tester, ViewLayoutPB.Board);
// validate the referenced board is inserted
expect(
@ -66,7 +64,7 @@ void main() {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
await insertReferenceDatabase(tester, ViewLayoutPB.Calendar);
await insertLinkedDatabase(tester, ViewLayoutPB.Calendar);
// validate the referenced grid is inserted
expect(
@ -129,7 +127,7 @@ void main() {
}
/// Insert a referenced database of [layout] into the document
Future<void> insertReferenceDatabase(
Future<void> insertLinkedDatabase(
WidgetTester tester,
ViewLayoutPB layout,
) async {
@ -150,7 +148,7 @@ Future<void> insertReferenceDatabase(
// insert a referenced view
await tester.editor.showSlashMenu();
await tester.editor.tapSlashMenuItemWithName(
layout.referencedMenuName,
layout.slashMenuLinkedName,
);
final linkToPageMenu = find.byType(InlineActionsHandler);
@ -176,16 +174,9 @@ Future<void> createInlineDatabase(
await tester.editor.tapLineOfEditorAt(0);
// insert a referenced view
await tester.editor.showSlashMenu();
final name = switch (layout) {
ViewLayoutPB.Grid => LocaleKeys.document_slashMenu_grid_createANewGrid.tr(),
ViewLayoutPB.Board =>
LocaleKeys.document_slashMenu_board_createANewBoard.tr(),
ViewLayoutPB.Calendar =>
LocaleKeys.document_slashMenu_calendar_createANewCalendar.tr(),
_ => '',
};
await tester.editor.tapSlashMenuItemWithName(
name,
layout.slashMenuName,
offset: 100,
);
await tester.pumpAndSettle();

View File

@ -0,0 +1,169 @@
import 'dart:io';
import 'package:appflowy/core/config/kv.dart';
import 'package:appflowy/core/config/kv_keys.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/file/file_upload_menu.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import '../../shared/mock/mock_file_picker.dart';
import '../../shared/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
TestWidgetsFlutterBinding.ensureInitialized();
group('file block in document', () {
testWidgets('insert a file from local file + rename file', (tester) async {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
// create a new document
await tester.createNewPageWithNameUnderParent(name: 'Insert file test');
// tap the first line of the document
await tester.editor.tapLineOfEditorAt(0);
await tester.editor.showSlashMenu();
await tester.editor.tapSlashMenuItemWithName(
LocaleKeys.document_slashMenu_name_file.tr(),
);
expect(find.byType(FileBlockComponent), findsOneWidget);
await tester.tap(find.byType(FileBlockComponent));
await tester.pumpAndSettle(const Duration(seconds: 1));
expect(find.byType(FileUploadMenu), findsOneWidget);
final image = await rootBundle.load('assets/test/images/sample.jpeg');
final tempDirectory = await getTemporaryDirectory();
final filePath = p.join(tempDirectory.path, 'sample.jpeg');
final file = File(filePath)..writeAsBytesSync(image.buffer.asUint8List());
mockPickFilePaths(paths: [filePath]);
await getIt<KeyValueStorage>().set(KVKeys.kCloudType, '0');
await tester.tap(
find.text(LocaleKeys.document_plugins_file_fileUploadHint.tr()),
);
await tester.pumpAndSettle();
expect(find.byType(FileUploadMenu), findsNothing);
expect(find.byType(FileBlockComponent), findsOneWidget);
final node = tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
expect(node.type, FileBlockKeys.type);
expect(node.attributes[FileBlockKeys.url], isNotEmpty);
expect(
node.attributes[FileBlockKeys.urlType],
FileUrlType.local.toIntValue(),
);
// Check the name of the file is correctly extracted
expect(node.attributes[FileBlockKeys.name], 'sample.jpeg');
expect(find.text('sample.jpeg'), findsOneWidget);
const newName = "Renamed file";
// Hover on the widget to see the three dots to open FileBlockMenu
await tester.hoverOnWidget(
find.byType(FileBlockComponent),
onHover: () async {
await tester.tap(find.byType(FileMenuTrigger));
await tester.pumpAndSettle();
await tester.tap(
find.text(LocaleKeys.document_plugins_file_renameFile_title.tr()),
);
},
);
await tester.pumpAndSettle();
expect(find.byType(FlowyTextField), findsOneWidget);
await tester.enterText(find.byType(FlowyTextField), newName);
await tester.pump();
await tester.tap(find.text(LocaleKeys.button_save.tr()));
await tester.pumpAndSettle();
final updatedNode =
tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
expect(updatedNode.attributes[FileBlockKeys.name], newName);
expect(find.text(newName), findsOneWidget);
// remove the temp file
file.deleteSync();
});
testWidgets('insert a file from network', (tester) async {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
// create a new document
await tester.createNewPageWithNameUnderParent(name: 'Insert file test');
// tap the first line of the document
await tester.editor.tapLineOfEditorAt(0);
await tester.editor.showSlashMenu();
await tester.editor.tapSlashMenuItemWithName(
LocaleKeys.document_slashMenu_name_file.tr(),
);
expect(find.byType(FileBlockComponent), findsOneWidget);
await tester.tap(find.byType(FileBlockComponent));
await tester.pumpAndSettle(const Duration(seconds: 1));
expect(find.byType(FileUploadMenu), findsOneWidget);
// Navigate to integrate link tab
await tester.tapButtonWithName(
LocaleKeys.document_plugins_file_networkTab.tr(),
);
await tester.pumpAndSettle();
const url =
'https://images.unsplash.com/photo-1469474968028-56623f02e42e?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&dl=david-marcu-78A265wPiO4-unsplash.jpg&w=640';
await tester.enterText(
find.descendant(
of: find.byType(FileUploadMenu),
matching: find.byType(FlowyTextField),
),
url,
);
await tester.tapButton(
find.descendant(
of: find.byType(FileUploadMenu),
matching: find.text(
LocaleKeys.document_plugins_file_networkAction.tr(),
findRichText: true,
),
),
);
await tester.pumpAndSettle();
expect(find.byType(FileUploadMenu), findsNothing);
expect(find.byType(FileBlockComponent), findsOneWidget);
final node = tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
expect(node.type, FileBlockKeys.type);
expect(node.attributes[FileBlockKeys.url], isNotEmpty);
expect(
node.attributes[FileBlockKeys.urlType],
FileUrlType.network.toIntValue(),
);
// Check the name is correctly extracted from the url
expect(
node.attributes[FileBlockKeys.name],
'photo-1469474968028-56623f02e42e',
);
expect(find.text('photo-1469474968028-56623f02e42e'), findsOneWidget);
});
});
}

View File

@ -3,12 +3,12 @@ import 'dart:io';
import 'package:appflowy/core/config/kv.dart';
import 'package:appflowy/core/config/kv_keys.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/custom_image_block_component.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/embed_image_url_widget.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/custom_image_block_component/custom_image_block_component.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/image_placeholder.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/resizeable_image.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/unsplash_image_widget.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu/upload_image_menu.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu/widgets/embed_image_url_widget.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy_editor/appflowy_editor.dart'
hide UploadImageMenu, ResizableImage;
@ -36,13 +36,15 @@ void main() {
// create a new document
await tester.createNewPageWithNameUnderParent(
name: LocaleKeys.document_plugins_image_addAnImage.tr(),
name: LocaleKeys.document_plugins_image_addAnImageDesktop.tr(),
);
// tap the first line of the document
await tester.editor.tapLineOfEditorAt(0);
await tester.editor.showSlashMenu();
await tester.editor.tapSlashMenuItemWithName('Image');
await tester.editor.tapSlashMenuItemWithName(
LocaleKeys.document_slashMenu_name_image.tr(),
);
expect(find.byType(CustomImageBlockComponent), findsOneWidget);
expect(find.byType(ImagePlaceholder), findsOneWidget);
expect(
@ -84,13 +86,15 @@ void main() {
// create a new document
await tester.createNewPageWithNameUnderParent(
name: LocaleKeys.document_plugins_image_addAnImage.tr(),
name: LocaleKeys.document_plugins_image_addAnImageDesktop.tr(),
);
// tap the first line of the document
await tester.editor.tapLineOfEditorAt(0);
await tester.editor.showSlashMenu();
await tester.editor.tapSlashMenuItemWithName('Image');
await tester.editor.tapSlashMenuItemWithName(
LocaleKeys.document_slashMenu_name_image.tr(),
);
expect(find.byType(CustomImageBlockComponent), findsOneWidget);
expect(find.byType(ImagePlaceholder), findsOneWidget);
expect(
@ -137,13 +141,15 @@ void main() {
// create a new document
await tester.createNewPageWithNameUnderParent(
name: LocaleKeys.document_plugins_image_addAnImage.tr(),
name: LocaleKeys.document_plugins_image_addAnImageDesktop.tr(),
);
// tap the first line of the document
await tester.editor.tapLineOfEditorAt(0);
await tester.editor.showSlashMenu();
await tester.editor.tapSlashMenuItemWithName('Image');
await tester.editor.tapSlashMenuItemWithName(
LocaleKeys.document_slashMenu_name_image.tr(),
);
expect(find.byType(CustomImageBlockComponent), findsOneWidget);
expect(find.byType(ImagePlaceholder), findsOneWidget);
expect(
@ -161,5 +167,69 @@ void main() {
expect(find.byType(UnsplashImageWidget), findsOneWidget);
});
});
testWidgets('insert two images from local file at once', (tester) async {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
// create a new document
await tester.createNewPageWithNameUnderParent(
name: LocaleKeys.document_plugins_image_addAnImageDesktop.tr(),
);
// tap the first line of the document
await tester.editor.tapLineOfEditorAt(0);
await tester.editor.showSlashMenu();
await tester.editor.tapSlashMenuItemWithName(
LocaleKeys.document_slashMenu_name_image.tr(),
);
expect(find.byType(CustomImageBlockComponent), findsOneWidget);
expect(find.byType(ImagePlaceholder), findsOneWidget);
expect(
find.descendant(
of: find.byType(ImagePlaceholder),
matching: find.byType(AppFlowyPopover),
),
findsOneWidget,
);
expect(find.byType(UploadImageMenu), findsOneWidget);
final firstImage =
await rootBundle.load('assets/test/images/sample.jpeg');
final secondImage =
await rootBundle.load('assets/test/images/sample.gif');
final tempDirectory = await getTemporaryDirectory();
final firstImagePath = p.join(tempDirectory.path, 'sample.jpeg');
final firstFile = File(firstImagePath)
..writeAsBytesSync(firstImage.buffer.asUint8List());
final secondImagePath = p.join(tempDirectory.path, 'sample.gif');
final secondFile = File(secondImagePath)
..writeAsBytesSync(secondImage.buffer.asUint8List());
mockPickFilePaths(paths: [firstImagePath, secondImagePath]);
await getIt<KeyValueStorage>().set(KVKeys.kCloudType, '0');
await tester.tapButtonWithName(
LocaleKeys.document_imageBlock_upload_placeholder.tr(),
);
await tester.pumpAndSettle();
expect(find.byType(ResizableImage), findsNWidgets(2));
final firstNode =
tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
expect(firstNode.type, ImageBlockKeys.type);
expect(firstNode.attributes[ImageBlockKeys.url], isNotEmpty);
final secondNode =
tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
expect(secondNode.type, ImageBlockKeys.type);
expect(secondNode.attributes[ImageBlockKeys.url], isNotEmpty);
// remove the temp files
await Future.wait([firstFile.delete(), secondFile.delete()]);
});
});
}

View File

@ -33,7 +33,7 @@ void main() {
);
// tap the inline math equation button
final inlineMathEquationButton = find.byTooltip(
final inlineMathEquationButton = find.findFlowyTooltip(
LocaleKeys.document_plugins_createInlineMathEquation.tr(),
);
await tester.tapButton(inlineMathEquationButton);
@ -78,7 +78,7 @@ void main() {
);
// tap the inline math equation button
var inlineMathEquationButton = find.byTooltip(
var inlineMathEquationButton = find.findFlowyTooltip(
LocaleKeys.document_plugins_createInlineMathEquation.tr(),
);
await tester.tapButton(inlineMathEquationButton);
@ -93,11 +93,11 @@ void main() {
);
// expect to the see the inline math equation button is highlighted
inlineMathEquationButton = find.byWidgetPredicate(
(widget) =>
widget is SVGIconItemWidget &&
widget.tooltip ==
LocaleKeys.document_plugins_createInlineMathEquation.tr(),
inlineMathEquationButton = find.descendant(
of: find.findFlowyTooltip(
LocaleKeys.document_plugins_createInlineMathEquation.tr(),
),
matching: find.byType(SVGIconItemWidget),
);
expect(
tester.widget<SVGIconItemWidget>(inlineMathEquationButton).isHighlight,

View File

@ -1,7 +1,7 @@
import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_page_block.dart';
import 'package:appflowy/shared/flowy_error_page.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
import 'package:flowy_infra/uuid.dart';
import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
@ -92,7 +92,7 @@ void main() {
);
expect(finder, findsOneWidget);
await tester.tapButton(finder);
expect(find.byType(FlowyErrorPage), findsOneWidget);
expect(find.byType(AppFlowyErrorPage), findsOneWidget);
});
});
}

View File

@ -0,0 +1,293 @@
import 'dart:io';
import 'package:appflowy/core/config/kv.dart';
import 'package:appflowy/core/config/kv_keys.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/multi_image_block_component/image_render.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/multi_image_block_component/layouts/image_browser_layout.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/multi_image_block_component/multi_image_block_component.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/multi_image_block_component/multi_image_placeholder.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu/upload_image_menu.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu/widgets/embed_image_url_widget.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy/shared/appflowy_network_image.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/presentation/widgets/image_viewer/interactive_image_toolbar.dart';
import 'package:appflowy/workspace/presentation/widgets/image_viewer/interactive_image_viewer.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import '../../shared/mock/mock_file_picker.dart';
import '../../shared/util.dart';
import '../board/board_hide_groups_test.dart';
void main() {
setUp(() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
TestWidgetsFlutterBinding.ensureInitialized();
});
group('multi image block in document', () {
testWidgets('insert images from local and use interactive viewer',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
// create a new document
await tester.createNewPageWithNameUnderParent(
name: 'multi image block test',
);
// tap the first line of the document
await tester.editor.tapLineOfEditorAt(0);
await tester.editor.showSlashMenu();
await tester.editor.tapSlashMenuItemWithName(
LocaleKeys.document_slashMenu_name_photoGallery.tr(),
offset: 100,
);
expect(find.byType(MultiImageBlockComponent), findsOneWidget);
expect(find.byType(MultiImagePlaceholder), findsOneWidget);
await tester.tap(find.byType(MultiImagePlaceholder));
await tester.pumpAndSettle();
expect(find.byType(UploadImageMenu), findsOneWidget);
final firstImage =
await rootBundle.load('assets/test/images/sample.jpeg');
final secondImage =
await rootBundle.load('assets/test/images/sample.gif');
final tempDirectory = await getTemporaryDirectory();
final firstImagePath = p.join(tempDirectory.path, 'sample.jpeg');
final firstFile = File(firstImagePath)
..writeAsBytesSync(firstImage.buffer.asUint8List());
final secondImagePath = p.join(tempDirectory.path, 'sample.gif');
final secondFile = File(secondImagePath)
..writeAsBytesSync(secondImage.buffer.asUint8List());
mockPickFilePaths(paths: [firstImagePath, secondImagePath]);
await getIt<KeyValueStorage>().set(KVKeys.kCloudType, '0');
await tester.tapButtonWithName(
LocaleKeys.document_imageBlock_upload_placeholder.tr(),
);
await tester.pumpAndSettle();
expect(find.byType(ImageBrowserLayout), findsOneWidget);
final node = tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
expect(node.type, MultiImageBlockKeys.type);
final data = MultiImageData.fromJson(
node.attributes[MultiImageBlockKeys.images],
);
expect(data.images.length, 2);
// Start using the interactive viewer to view the image(s)
final imageFinder = find
.byWidgetPredicate(
(w) =>
w is Image &&
w.image is FileImage &&
(w.image as FileImage).file.path.endsWith('.jpeg'),
)
.first;
await tester.tap(imageFinder);
await tester.pump(kDoubleTapMinTime);
await tester.tap(imageFinder);
await tester.pumpAndSettle();
final ivFinder = find.byType(InteractiveImageViewer);
expect(ivFinder, findsOneWidget);
// go to next image
await tester.tap(find.byFlowySvg(FlowySvgs.arrow_right_s));
await tester.pumpAndSettle();
// Expect image to end with .gif
final gifImageFinder = find.byWidgetPredicate(
(w) =>
w is Image &&
w.image is FileImage &&
(w.image as FileImage).file.path.endsWith('.gif'),
);
gifImageFinder.evaluate();
expect(gifImageFinder.found.length, 2);
// go to previous image
await tester.tap(find.byFlowySvg(FlowySvgs.arrow_left_s));
await tester.pumpAndSettle();
gifImageFinder.evaluate();
expect(gifImageFinder.found.length, 1);
// remove the temp files
await Future.wait([firstFile.delete(), secondFile.delete()]);
});
testWidgets('insert and delete images from network', (tester) async {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
// create a new document
await tester.createNewPageWithNameUnderParent(
name: 'multi image block test',
);
// tap the first line of the document
await tester.editor.tapLineOfEditorAt(0);
await tester.editor.showSlashMenu();
await tester.editor.tapSlashMenuItemWithName(
LocaleKeys.document_slashMenu_name_photoGallery.tr(),
offset: 100,
);
expect(find.byType(MultiImageBlockComponent), findsOneWidget);
expect(find.byType(MultiImagePlaceholder), findsOneWidget);
await tester.tap(find.byType(MultiImagePlaceholder));
await tester.pumpAndSettle();
expect(find.byType(UploadImageMenu), findsOneWidget);
await tester.tapButtonWithName(
LocaleKeys.document_imageBlock_embedLink_label.tr(),
);
const url =
'https://images.unsplash.com/photo-1469474968028-56623f02e42e?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&dl=david-marcu-78A265wPiO4-unsplash.jpg&w=640';
await tester.enterText(
find.descendant(
of: find.byType(EmbedImageUrlWidget),
matching: find.byType(TextField),
),
url,
);
await tester.pumpAndSettle();
await tester.tapButton(
find.descendant(
of: find.byType(EmbedImageUrlWidget),
matching: find.text(
LocaleKeys.document_imageBlock_embedLink_label.tr(),
findRichText: true,
),
),
);
await tester.pumpAndSettle();
expect(find.byType(ImageBrowserLayout), findsOneWidget);
final node = tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
expect(node.type, MultiImageBlockKeys.type);
final data = MultiImageData.fromJson(
node.attributes[MultiImageBlockKeys.images],
);
expect(data.images.length, 1);
final imageFinder = find
.byWidgetPredicate(
(w) => w is FlowyNetworkImage && w.url == url,
)
.first;
// Insert two images from network
for (int i = 0; i < 2; i++) {
// Hover on the image to show the image toolbar
await tester.hoverOnWidget(
imageFinder,
onHover: () async {
// Click on the add
final addFinder = find.descendant(
of: find.byType(MultiImageMenu),
matching: find.byFlowySvg(FlowySvgs.add_s),
);
expect(addFinder, findsOneWidget);
await tester.tap(addFinder);
await tester.pumpAndSettle();
await tester.tapButtonWithName(
LocaleKeys.document_imageBlock_embedLink_label.tr(),
);
await tester.enterText(
find.descendant(
of: find.byType(EmbedImageUrlWidget),
matching: find.byType(TextField),
),
url,
);
await tester.pumpAndSettle();
await tester.tapButton(
find.descendant(
of: find.byType(EmbedImageUrlWidget),
matching: find.text(
LocaleKeys.document_imageBlock_embedLink_label.tr(),
findRichText: true,
),
),
);
await tester.pumpAndSettle();
},
);
}
await tester.pumpAndSettle();
// There should be 4 images visible now, where 2 are thumbnails
expect(find.byType(ThumbnailItem), findsNWidgets(3));
// And all three use ImageRender
expect(find.byType(ImageRender), findsNWidgets(4));
// Hover on and delete the first thumbnail image
await tester.hoverOnWidget(find.byType(ThumbnailItem).first);
final deleteFinder = find
.descendant(
of: find.byType(ThumbnailItem),
matching: find.byFlowySvg(FlowySvgs.delete_s),
)
.first;
expect(deleteFinder, findsOneWidget);
await tester.tap(deleteFinder);
await tester.pumpAndSettle();
expect(find.byType(ImageRender), findsNWidgets(3));
// Delete one from interactive viewer
await tester.tap(imageFinder);
await tester.pump(kDoubleTapMinTime);
await tester.tap(imageFinder);
await tester.pumpAndSettle();
final ivFinder = find.byType(InteractiveImageViewer);
expect(ivFinder, findsOneWidget);
await tester.tap(
find.descendant(
of: find.byType(InteractiveImageToolbar),
matching: find.byFlowySvg(FlowySvgs.delete_s),
),
);
await tester.pumpAndSettle();
expect(find.byType(InteractiveImageViewer), findsNothing);
// There should be 1 image and the thumbnail for said image still visible
expect(find.byType(ImageRender), findsNWidgets(2));
});
});
}

View File

@ -171,7 +171,8 @@ Future<void> insertOutlineInDocument(WidgetTester tester) async {
// open the actions menu and insert the outline block
await tester.editor.showSlashMenu();
await tester.editor.tapSlashMenuItemWithName(
LocaleKeys.document_selectionMenu_outline.tr(),
LocaleKeys.document_slashMenu_name_outline.tr(),
offset: 100,
);
await tester.pumpAndSettle();
}

View File

@ -8,9 +8,10 @@ void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('Empty', () {
testWidgets('toggle theme mode', (tester) async {
testWidgets('empty test', (tester) async {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
await tester.wait(1000);
});
});
}

View File

@ -82,11 +82,11 @@ void main() {
HeadingBlockKeys.type,
);
expect(
importedPageEditorState.getNodeAtPath([2])!.type,
importedPageEditorState.getNodeAtPath([1])!.type,
HeadingBlockKeys.type,
);
expect(
importedPageEditorState.getNodeAtPath([4])!.type,
importedPageEditorState.getNodeAtPath([2])!.type,
TableBlockKeys.type,
);
});

View File

@ -1,113 +0,0 @@
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/ai_client.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor/src/render/toolbar/toolbar_widget.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import '../../shared/mock/mock_openai_repository.dart';
import '../../shared/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
const service = TestWorkspaceService(TestWorkspace.aiWorkSpace);
group('integration tests for open-ai smart menu', () {
setUpAll(() async => service.setUpAll());
setUp(() async => service.setUp());
testWidgets('testing selection on open-ai smart menu replace',
(tester) async {
final appFlowyEditor = await setUpOpenAITesting(tester);
final editorState = appFlowyEditor.editorState;
editorState.service.selectionService.updateSelection(
Selection(
start: Position(path: [1], offset: 4),
end: Position(path: [1], offset: 10),
),
);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
await tester.pumpAndSettle();
expect(find.byType(ToolbarWidget), findsAtLeastNWidgets(1));
await tester.tap(find.byTooltip('AI Assistants'));
await tester.pumpAndSettle(const Duration(milliseconds: 500));
await tester.tap(find.text('Summarize'));
await tester.pumpAndSettle();
await tester
.tap(find.byType(FlowyRichTextButton, skipOffstage: false).first);
await tester.pumpAndSettle();
expect(
editorState.service.selectionService.currentSelection.value,
Selection(
start: Position(path: [1], offset: 4),
end: Position(path: [1], offset: 84),
),
);
});
testWidgets('testing selection on open-ai smart menu insert',
(tester) async {
final appFlowyEditor = await setUpOpenAITesting(tester);
final editorState = appFlowyEditor.editorState;
editorState.service.selectionService.updateSelection(
Selection(
start: Position(path: [1]),
end: Position(path: [1], offset: 5),
),
);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
await tester.pumpAndSettle();
expect(find.byType(ToolbarWidget), findsAtLeastNWidgets(1));
await tester.tap(find.byTooltip('AI Assistants'));
await tester.pumpAndSettle(const Duration(milliseconds: 500));
await tester.tap(find.text('Summarize'));
await tester.pumpAndSettle();
await tester
.tap(find.byType(FlowyRichTextButton, skipOffstage: false).at(1));
await tester.pumpAndSettle();
expect(
editorState.service.selectionService.currentSelection.value,
Selection(
start: Position(path: [2]),
end: Position(path: [3]),
),
);
});
});
}
Future<AppFlowyEditor> setUpOpenAITesting(WidgetTester tester) async {
await tester.initializeAppFlowy();
await mockOpenAIRepository();
await simulateKeyDownEvent(LogicalKeyboardKey.controlLeft);
await simulateKeyDownEvent(LogicalKeyboardKey.backslash);
await tester.pumpAndSettle();
final Finder editor = find.byType(AppFlowyEditor);
await tester.tap(editor);
await tester.pumpAndSettle();
return tester.state(editor).widget as AppFlowyEditor;
}
Future<void> mockOpenAIRepository() async {
await getIt.unregister<AIRepository>();
getIt.registerFactoryAsync<AIRepository>(
() => Future.value(
MockOpenAIRepository(),
),
);
return;
}

View File

@ -1,6 +1,6 @@
import 'dart:io';
import 'package:appflowy/plugins/document/presentation/share/share_button.dart';
import 'package:appflowy/plugins/shared/share/share_button.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:path/path.dart' as p;
@ -51,13 +51,13 @@ void main() {
},
);
final shareButton = find.byType(ShareActionList);
final shareButtonState =
tester.state(shareButton) as ShareActionListState;
final shareButton = find.byType(ShareButton);
final shareButtonState = tester.widget(shareButton) as ShareButton;
final path = await mockSaveFilePath(
p.join(
context.applicationDataDirectory,
'${shareButtonState.name}.md',
'${shareButtonState.view.name}.md',
),
);

View File

@ -1,19 +1,19 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
import 'package:appflowy/workspace/presentation/settings/pages/settings_account_view.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_supabase_cloud.dart';
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'util.dart';
extension AppFlowyAuthTest on WidgetTester {
Future<void> tapGoogleLoginInButton() async {
await tapButton(find.byKey(const Key('signInWithGoogleButton')));
await tapButton(
find.byKey(signInWithGoogleButtonKey),
);
}
/// Requires being on the SettingsPage.account of the SettingsDialog
@ -27,8 +27,8 @@ extension AppFlowyAuthTest on WidgetTester {
await tapButton(find.byType(SignInOutButton));
expectToSeeText(LocaleKeys.button_confirm.tr());
await tapButtonWithName(LocaleKeys.button_confirm.tr());
expectToSeeText(LocaleKeys.button_ok.tr());
await tapButtonWithName(LocaleKeys.button_ok.tr());
}
Future<void> tapSignInAsGuest() async {
@ -36,7 +36,7 @@ extension AppFlowyAuthTest on WidgetTester {
}
void expectToSeeGoogleLoginButton() {
expect(find.byKey(const Key('signInWithGoogleButton')), findsOneWidget);
expect(find.byKey(signInWithGoogleButtonKey), findsOneWidget);
}
void assertSwitchValue(Finder finder, bool value) {
@ -51,26 +51,6 @@ extension AppFlowyAuthTest on WidgetTester {
assert(isSwitched == value);
}
void assertEnableEncryptSwitchValue(bool value) {
assertSwitchValue(
find.descendant(
of: find.byType(EnableEncrypt),
matching: find.byWidgetPredicate((widget) => widget is Switch),
),
value,
);
}
void assertSupabaseEnableSyncSwitchValue(bool value) {
assertSwitchValue(
find.descendant(
of: find.byType(SupabaseEnableSync),
matching: find.byWidgetPredicate((widget) => widget is Switch),
),
value,
);
}
void assertAppFlowyCloudEnableSyncSwitchValue(bool value) {
assertToggleValue(
find.descendant(
@ -81,15 +61,6 @@ extension AppFlowyAuthTest on WidgetTester {
);
}
Future<void> toggleEnableEncrypt() async {
final finder = find.descendant(
of: find.byType(EnableEncrypt),
matching: find.byWidgetPredicate((widget) => widget is Switch),
);
await tapButton(finder);
}
Future<void> toggleEnableSync(Type syncButton) async {
final finder = find.descendant(
of: find.byType(syncButton),

View File

@ -1,21 +1,19 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/gestures.dart';
import 'package:flutter/services.dart';
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/env/cloud_env_test.dart';
import 'package:appflowy/startup/entry_point.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/af_cloud_mock_auth_service.dart';
import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/user/application/auth/supabase_mock_auth_service.dart';
import 'package:appflowy/user/presentation/presentation.dart';
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:flowy_infra/uuid.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
@ -56,8 +54,6 @@ extension AppFlowyTestBase on WidgetTester {
switch (cloudType) {
case AuthenticatorType.local:
break;
case AuthenticatorType.supabase:
break;
case AuthenticatorType.appflowyCloudSelfHost:
rustEnvs["GOTRUE_ADMIN_EMAIL"] = "admin@example.com";
rustEnvs["GOTRUE_ADMIN_PASSWORD"] = "password";
@ -76,13 +72,6 @@ extension AppFlowyTestBase on WidgetTester {
case AuthenticatorType.local:
await useLocalServer();
break;
case AuthenticatorType.supabase:
await useTestSupabaseCloud();
getIt.unregister<AuthService>();
getIt.registerFactory<AuthService>(
() => SupabaseMockAuthService(),
);
break;
case AuthenticatorType.appflowyCloudSelfHost:
await useTestSelfHostedAppFlowyCloud();
getIt.unregister<AuthService>();
@ -231,13 +220,16 @@ extension AppFlowyFinderTestBase on CommonFinders {
(widget) => widget is FlowyText && widget.text == text,
);
}
}
Future<void> useTestSupabaseCloud() async {
await useSupabaseCloud(
url: TestEnv.supabaseUrl,
anonKey: TestEnv.supabaseAnonKey,
);
Finder findFlowyTooltip(String richMessage, {bool skipOffstage = true}) {
return byWidgetPredicate(
(widget) =>
widget is FlowyTooltip &&
widget.richMessage != null &&
widget.richMessage!.toPlainText().contains(richMessage),
skipOffstage: skipOffstage,
);
}
}
Future<void> useTestSelfHostedAppFlowyCloud() async {

View File

@ -1,17 +1,12 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:appflowy/core/config/kv.dart';
import 'package:appflowy/core/config/kv_keys.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/presentation.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/emoji_picker_button.dart';
import 'package:appflowy/plugins/document/presentation/share/share_button.dart';
import 'package:appflowy/plugins/shared/share/share_button.dart';
import 'package:appflowy/shared/feature_flags.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/presentation/screens/screens.dart';
@ -35,6 +30,10 @@ import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/widget/buttons/primary_button.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'emoji.dart';
@ -259,7 +258,7 @@ extension CommonOperations on WidgetTester {
/// Tap the share button above the document page.
Future<void> tapShareButton() async {
final shareButton = find.byWidgetPredicate(
(widget) => widget is DocumentShareButton,
(widget) => widget is ShareButton,
);
await tapButton(shareButton);
}
@ -663,4 +662,34 @@ extension ViewLayoutPBTest on ViewLayoutPB {
throw UnsupportedError('Unsupported layout: $this');
}
}
String get slashMenuName {
switch (this) {
case ViewLayoutPB.Grid:
return LocaleKeys.document_slashMenu_name_grid.tr();
case ViewLayoutPB.Board:
return LocaleKeys.document_slashMenu_name_kanban.tr();
case ViewLayoutPB.Document:
return LocaleKeys.document_slashMenu_name_doc.tr();
case ViewLayoutPB.Calendar:
return LocaleKeys.document_slashMenu_name_calendar.tr();
default:
throw UnsupportedError('Unsupported layout: $this');
}
}
String get slashMenuLinkedName {
switch (this) {
case ViewLayoutPB.Grid:
return LocaleKeys.document_slashMenu_name_linkedGrid.tr();
case ViewLayoutPB.Board:
return LocaleKeys.document_slashMenu_name_linkedKanban.tr();
case ViewLayoutPB.Document:
return LocaleKeys.document_slashMenu_name_linkedDoc.tr();
case ViewLayoutPB.Calendar:
return LocaleKeys.document_slashMenu_name_linkedCalendar.tr();
default:
throw UnsupportedError('Unsupported layout: $this');
}
}
}

View File

@ -3,15 +3,15 @@ import 'dart:ui';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/base/emoji/emoji_picker.dart';
import 'package:appflowy/plugins/base/emoji/emoji_skin_tone.dart';
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_add_button.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_option_button.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/cover_editor.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/embed_image_url_widget.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu/widgets/embed_image_url_widget.dart';
import 'package:appflowy/plugins/inline_actions/widgets/inline_actions_handler.dart';
import 'package:appflowy/shared/icon_emoji_picker/emoji_skin_tone.dart';
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
@ -84,7 +84,7 @@ class EditorOperations {
final Finder button = !isInPicker
? find.text(LocaleKeys.document_plugins_cover_removeIcon.tr())
: find.descendant(
of: find.byType(FlowyIconPicker),
of: find.byType(FlowyIconEmojiPicker),
matching: find.text(LocaleKeys.button_remove.tr()),
);
await tester.tapButton(button);
@ -170,8 +170,26 @@ class EditorOperations {
/// Tap the slash menu item with [name]
///
/// Must call [showSlashMenu] first.
Future<void> tapSlashMenuItemWithName(String name) async {
Future<void> tapSlashMenuItemWithName(
String name, {
double offset = 200,
}) async {
final slashMenu = find
.ancestor(
of: find.byType(SelectionMenuItemWidget),
matching: find.byWidgetPredicate(
(widget) => widget is Scrollable,
),
)
.first;
final slashMenuItem = find.text(name, findRichText: true);
await tester.scrollUntilVisible(
slashMenuItem,
offset,
scrollable: slashMenu,
duration: const Duration(milliseconds: 250),
);
assert(slashMenuItem.hasFound);
await tester.tapButton(slashMenuItem);
}

View File

@ -12,17 +12,23 @@ import 'package:flowy_infra_ui/style_widget/text_field.dart';
import 'package:flutter_test/flutter_test.dart';
import '../desktop/board/board_hide_groups_test.dart';
import 'base.dart';
import 'common_operations.dart';
extension AppFlowySettings on WidgetTester {
/// Open settings page
Future<void> openSettings() async {
final settingsDialog = find.byType(SettingsDialog);
// tap empty area to close the settings page
while (settingsDialog.evaluate().isNotEmpty) {
await tapAt(Offset.zero);
await pumpAndSettle();
}
final settingsButton = find.byType(UserSettingButton);
expect(settingsButton, findsOneWidget);
await tapButton(settingsButton);
final settingsDialog = find.byType(SettingsDialog);
expect(settingsDialog, findsOneWidget);
return;
}
@ -74,7 +80,7 @@ extension AppFlowySettings on WidgetTester {
of: find.byType(UserProfileSetting),
matching: find.byFlowySvg(FlowySvgs.edit_s),
);
await tap(editUsernameFinder);
await tap(editUsernameFinder, warnIfMissed: false);
await pumpAndSettle();
final userNameFinder = find.descendant(

View File

@ -1,5 +1,5 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_actions.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_icon.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart';
@ -58,7 +58,7 @@ extension AppFlowyWorkspace on WidgetTester {
);
expect(iconButton, findsOneWidget);
await tapButton(iconButton);
final iconPicker = find.byType(FlowyIconPicker);
final iconPicker = find.byType(FlowyIconEmojiPicker);
expect(iconPicker, findsOneWidget);
await tapButton(find.findTextInFlowyText(icon));
}

View File

@ -48,8 +48,6 @@ PODS:
- fluttertoast (0.0.2):
- Flutter
- Toast
- image_gallery_saver (2.0.2):
- Flutter
- image_picker_ios (0.0.1):
- Flutter
- integration_test (0.0.1):
@ -69,6 +67,11 @@ PODS:
- SDWebImage (5.14.2):
- SDWebImage/Core (= 5.14.2)
- SDWebImage/Core (5.14.2)
- Sentry/HybridSDK (8.33.0)
- sentry_flutter (8.7.0):
- Flutter
- FlutterMacOS
- Sentry/HybridSDK (= 8.33.0)
- share_plus (0.0.1):
- Flutter
- shared_preferences_foundation (0.0.1):
@ -93,7 +96,6 @@ DEPENDENCIES:
- flowy_infra_ui (from `.symlinks/plugins/flowy_infra_ui/ios`)
- Flutter (from `Flutter`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- image_gallery_saver (from `.symlinks/plugins/image_gallery_saver/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- integration_test (from `.symlinks/plugins/integration_test/ios`)
- irondash_engine_context (from `.symlinks/plugins/irondash_engine_context/ios`)
@ -101,6 +103,7 @@ DEPENDENCIES:
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
@ -113,6 +116,7 @@ SPEC REPOS:
- DKPhotoGallery
- ReachabilitySwift
- SDWebImage
- Sentry
- SwiftyGif
- Toast
@ -133,8 +137,6 @@ EXTERNAL SOURCES:
:path: Flutter
fluttertoast:
:path: ".symlinks/plugins/fluttertoast/ios"
image_gallery_saver:
:path: ".symlinks/plugins/image_gallery_saver/ios"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
integration_test:
@ -149,6 +151,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
sentry_flutter:
:path: ".symlinks/plugins/sentry_flutter/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation:
@ -170,8 +174,7 @@ SPEC CHECKSUMS:
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
irondash_engine_context: 3458bf979b90d616ffb8ae03a150bafe2e860cc9
@ -181,6 +184,8 @@ SPEC CHECKSUMS:
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84
Sentry: 8560050221424aef0bebc8e31eedf00af80f90a6
sentry_flutter: e26b861f744e5037a3faf9bf56603ec65d658a61
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
@ -191,4 +196,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: d0d9b4ff572d8695c38eb3f9b490f55cdfc57eca
COCOAPODS: 1.11.3
COCOAPODS: 1.15.2

View File

@ -372,6 +372,7 @@
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = AppFlowy;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -383,6 +384,8 @@
STRIP_STYLE = "non-global";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
@ -511,6 +514,7 @@
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = AppFlowy;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -522,6 +526,8 @@
STRIP_STYLE = "non-global";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@ -545,6 +551,7 @@
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = AppFlowy;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@ -556,6 +563,8 @@
STRIP_STYLE = "non-global";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;

View File

@ -1,75 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSCameraUsageDescription</key>
<string>AppFlowy requires access to the camera.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>AppFlowy requires access to the photo library.</string>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true />
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
</array>
<key>FLTEnableImpeller</key>
<false />
<key>CFBundleName</key>
<string>AppFlowy</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string></string>
<key>CFBundleURLSchemes</key>
<array>
<string>appflowy-flutter</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true />
<key>UIApplicationSupportsIndirectInputEvents</key>
<true />
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false />
<key>NSAppTransportSecurity</key>
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
</array>
<key>CFBundleName</key>
<string>AppFlowy</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>CFBundleURLName</key>
<string></string>
<key>CFBundleURLSchemes</key>
<array>
<string>appflowy-flutter</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>FLTEnableImpeller</key>
<false/>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</plist>
<key>NSCameraUsageDescription</key>
<string>AppFlowy requires access to the camera.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>AppFlowy requires access to the photo library.</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>

View File

@ -4,5 +4,9 @@
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.applesignin</key>
<array>
<string>Default</string>
</array>
</dict>
</plist>

View File

@ -3,16 +3,6 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/shared/window_title_bar.dart';
import 'package:appflowy/util/theme_extension.dart';
import 'package:appflowy/workspace/application/home/home_setting_bloc.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class CocoaWindowChannel {
CocoaWindowChannel._();
@ -40,11 +30,9 @@ class MoveWindowDetector extends StatefulWidget {
const MoveWindowDetector({
super.key,
this.child,
this.showTitleBar = false,
});
final Widget? child;
final bool showTitleBar;
@override
MoveWindowDetectorState createState() => MoveWindowDetectorState();
@ -56,28 +44,10 @@ class MoveWindowDetectorState extends State<MoveWindowDetector> {
@override
Widget build(BuildContext context) {
if (!Platform.isMacOS && !Platform.isWindows) {
if (!Platform.isMacOS) {
return widget.child ?? const SizedBox.shrink();
}
if (Platform.isWindows) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
if (widget.showTitleBar) ...[
WindowTitleBar(
leftChildren: [
_buildToggleMenuButton(context),
],
),
] else ...[
const SizedBox(height: 5),
],
widget.child ?? const SizedBox.shrink(),
],
);
}
return GestureDetector(
// https://stackoverflow.com/questions/52965799/flutter-gesturedetector-not-working-with-containers-in-stack
behavior: HitTestBehavior.translucent,
@ -98,47 +68,4 @@ class MoveWindowDetectorState extends State<MoveWindowDetector> {
child: widget.child,
);
}
Widget _buildToggleMenuButton(BuildContext context) {
if (!context.read<HomeSettingBloc>().state.isMenuCollapsed) {
return const SizedBox.shrink();
}
final color = Theme.of(context).isLightMode ? Colors.white : Colors.black;
final textSpan = TextSpan(
children: [
TextSpan(
text: '${LocaleKeys.sideBar_openSidebar.tr()}\n',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(color: color),
),
TextSpan(
text: Platform.isMacOS ? '⌘+.' : 'Ctrl+\\',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: Theme.of(context).hintColor),
),
],
);
return FlowyTooltip(
richMessage: textSpan,
child: Listener(
behavior: HitTestBehavior.translucent,
onPointerDown: (_) => context
.read<HomeSettingBloc>()
.add(const HomeSettingEvent.collapseMenu()),
child: FlowyHover(
child: Container(
width: 24,
padding: const EdgeInsets.all(4),
child: const RotatedBox(
quarterTurns: 2,
child: FlowySvg(FlowySvgs.hide_menu_s),
),
),
),
),
);
}
}

View File

@ -13,7 +13,6 @@ class AppFlowyConfiguration {
required this.device_id,
required this.platform,
required this.authenticator_type,
required this.supabase_config,
required this.appflowy_cloud_config,
required this.envs,
});
@ -28,41 +27,12 @@ class AppFlowyConfiguration {
final String device_id;
final String platform;
final int authenticator_type;
final SupabaseConfiguration supabase_config;
final AppFlowyCloudConfiguration appflowy_cloud_config;
final Map<String, String> envs;
Map<String, dynamic> toJson() => _$AppFlowyConfigurationToJson(this);
}
@JsonSerializable()
class SupabaseConfiguration {
SupabaseConfiguration({
required this.url,
required this.anon_key,
});
factory SupabaseConfiguration.fromJson(Map<String, dynamic> json) =>
_$SupabaseConfigurationFromJson(json);
/// Indicates whether the sync feature is enabled.
final String url;
final String anon_key;
Map<String, dynamic> toJson() => _$SupabaseConfigurationToJson(this);
static SupabaseConfiguration defaultConfig() {
return SupabaseConfiguration(
url: '',
anon_key: '',
);
}
bool get isValid {
return url.isNotEmpty && anon_key.isNotEmpty;
}
}
@JsonSerializable()
class AppFlowyCloudConfiguration {
AppFlowyCloudConfiguration({

View File

@ -21,9 +21,6 @@ Future<void> _setAuthenticatorType(AuthenticatorType ty) async {
case AuthenticatorType.local:
await getIt<KeyValueStorage>().set(KVKeys.kCloudType, 0.toString());
break;
case AuthenticatorType.supabase:
await getIt<KeyValueStorage>().set(KVKeys.kCloudType, 1.toString());
break;
case AuthenticatorType.appflowyCloud:
await getIt<KeyValueStorage>().set(KVKeys.kCloudType, 2.toString());
break;
@ -63,8 +60,6 @@ Future<AuthenticatorType> getAuthenticatorType() async {
switch (value ?? "0") {
case "0":
return AuthenticatorType.local;
case "1":
return AuthenticatorType.supabase;
case "2":
return AuthenticatorType.appflowyCloud;
case "3":
@ -93,10 +88,6 @@ Future<AuthenticatorType> getAuthenticatorType() async {
/// Returns `false` otherwise.
bool get isAuthEnabled {
final env = getIt<AppFlowyCloudSharedEnv>();
if (env.authenticatorType == AuthenticatorType.supabase) {
return env.supabaseConfig.isValid;
}
if (env.authenticatorType.isAppFlowyCloudEnabled) {
return env.appflowyCloudConfig.isValid;
}
@ -104,19 +95,6 @@ bool get isAuthEnabled {
return false;
}
/// Checks if Supabase is enabled.
///
/// This getter evaluates if Supabase should be enabled based on the
/// current integration mode and cloud type setting.
///
/// Returns:
/// A boolean value indicating whether Supabase is enabled. It returns `true`
/// if the application is in release or develop mode and the current cloud type
/// is `CloudType.supabase`. Otherwise, it returns `false`.
bool get isSupabaseEnabled {
return currentCloudType().isSupabaseEnabled;
}
/// Determines if AppFlowy Cloud is enabled.
bool get isAppFlowyCloudEnabled {
return currentCloudType().isAppFlowyCloudEnabled;
@ -124,7 +102,6 @@ bool get isAppFlowyCloudEnabled {
enum AuthenticatorType {
local,
supabase,
appflowyCloud,
appflowyCloudSelfHost,
// The 'appflowyCloudDevelop' type is used for develop purposes only.
@ -137,14 +114,10 @@ enum AuthenticatorType {
this == AuthenticatorType.appflowyCloudDevelop ||
this == AuthenticatorType.appflowyCloud;
bool get isSupabaseEnabled => this == AuthenticatorType.supabase;
int get value {
switch (this) {
case AuthenticatorType.local:
return 0;
case AuthenticatorType.supabase:
return 1;
case AuthenticatorType.appflowyCloud:
return 2;
case AuthenticatorType.appflowyCloudSelfHost:
@ -158,8 +131,6 @@ enum AuthenticatorType {
switch (value) {
case 0:
return AuthenticatorType.local;
case 1:
return AuthenticatorType.supabase;
case 2:
return AuthenticatorType.appflowyCloud;
case 3:
@ -197,25 +168,15 @@ Future<void> useLocalServer() async {
await _setAuthenticatorType(AuthenticatorType.local);
}
Future<void> useSupabaseCloud({
required String url,
required String anonKey,
}) async {
await _setAuthenticatorType(AuthenticatorType.supabase);
await setSupabaseServer(url, anonKey);
}
/// Use getIt<AppFlowyCloudSharedEnv>() to get the shared environment.
class AppFlowyCloudSharedEnv {
AppFlowyCloudSharedEnv({
required AuthenticatorType authenticatorType,
required this.appflowyCloudConfig,
required this.supabaseConfig,
}) : _authenticatorType = authenticatorType;
final AuthenticatorType _authenticatorType;
final AppFlowyCloudConfiguration appflowyCloudConfig;
final SupabaseConfiguration supabaseConfig;
AuthenticatorType get authenticatorType => _authenticatorType;
@ -229,10 +190,6 @@ class AppFlowyCloudSharedEnv {
? await getAppFlowyCloudConfig(authenticatorType)
: AppFlowyCloudConfiguration.defaultConfig();
final supabaseCloudConfig = authenticatorType.isSupabaseEnabled
? await getSupabaseCloudConfig()
: SupabaseConfiguration.defaultConfig();
// In the backend, the value '2' represents the use of AppFlowy Cloud. However, in the frontend,
// we distinguish between [AuthenticatorType.appflowyCloudSelfHost] and [AuthenticatorType.appflowyCloud].
// When the cloud type is [AuthenticatorType.appflowyCloudSelfHost] in the frontend, it should be
@ -244,7 +201,6 @@ class AppFlowyCloudSharedEnv {
return AppFlowyCloudSharedEnv(
authenticatorType: authenticatorType,
appflowyCloudConfig: appflowyCloudConfig,
supabaseConfig: supabaseCloudConfig,
);
} else {
// Using the cloud settings from the .env file.
@ -257,7 +213,6 @@ class AppFlowyCloudSharedEnv {
return AppFlowyCloudSharedEnv(
authenticatorType: AuthenticatorType.fromValue(Env.authenticatorType),
appflowyCloudConfig: appflowyCloudConfig,
supabaseConfig: SupabaseConfiguration.defaultConfig(),
);
}
}
@ -265,8 +220,7 @@ class AppFlowyCloudSharedEnv {
@override
String toString() {
return 'authenticator: $_authenticatorType\n'
'appflowy: ${appflowyCloudConfig.toJson()}\n'
'supabase: ${supabaseConfig.toJson()})\n';
'appflowy: ${appflowyCloudConfig.toJson()}\n';
}
}
@ -354,22 +308,3 @@ Future<void> setSupabaseServer(
await getIt<KeyValueStorage>().set(KVKeys.kSupabaseAnonKey, anonKey);
}
}
Future<SupabaseConfiguration> getSupabaseCloudConfig() async {
final url = await _getSupabaseUrl();
final anonKey = await _getSupabaseAnonKey();
return SupabaseConfiguration(
url: url,
anon_key: anonKey,
);
}
Future<String> _getSupabaseUrl() async {
final result = await getIt<KeyValueStorage>().get(KVKeys.kSupabaseURL);
return result ?? '';
}
Future<String> _getSupabaseAnonKey() async {
final result = await getIt<KeyValueStorage>().get(KVKeys.kSupabaseAnonKey);
return result ?? '';
}

View File

@ -29,4 +29,18 @@ abstract class Env {
defaultValue: '',
)
static const String afCloudUrl = _Env.afCloudUrl;
@EnviedField(
obfuscate: false,
varName: 'INTERNAL_BUILD',
defaultValue: '',
)
static const String internalBuild = _Env.internalBuild;
@EnviedField(
obfuscate: false,
varName: 'SENTRY_DSN',
defaultValue: '',
)
static const String sentryDsn = _Env.sentryDsn;
}

View File

@ -2,27 +2,41 @@ import 'dart:async';
import 'dart:convert';
import 'package:appflowy/mobile/presentation/chat/mobile_chat_screen.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
import 'package:flutter/material.dart';
import 'package:appflowy/mobile/presentation/database/board/mobile_board_screen.dart';
import 'package:appflowy/mobile/presentation/database/mobile_calendar_screen.dart';
import 'package:appflowy/mobile/presentation/database/mobile_grid_screen.dart';
import 'package:appflowy/mobile/presentation/presentation.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/recent/cached_recent_service.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
extension MobileRouter on BuildContext {
Future<void> pushView(ViewPB view, [Map<String, dynamic>? arguments]) async {
Future<void> pushView(
ViewPB view, {
Map<String, dynamic>? arguments,
bool addInRecent = true,
bool showMoreButton = true,
String? fixedTitle,
}) async {
// set the current view before pushing the new view
getIt<MenuSharedState>().latestOpenView = view;
unawaited(getIt<CachedRecentService>().updateRecentViews([view.id], true));
final queryParameters = view.queryParameters(arguments);
if (view.layout == ViewLayoutPB.Document) {
queryParameters[MobileDocumentScreen.viewShowMoreButton] =
showMoreButton.toString();
if (fixedTitle != null) {
queryParameters[MobileDocumentScreen.viewFixedTitle] = fixedTitle;
}
}
final uri = Uri(
path: view.routeName,
queryParameters: view.queryParameters(arguments),
queryParameters: queryParameters,
).toString();
await push(uri);
}

View File

@ -0,0 +1,217 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/application/document_data_pb_extension.dart';
import 'package:appflowy/plugins/document/application/document_service.dart';
import 'package:appflowy/user/application/reminder/reminder_extension.dart';
import 'package:appflowy/workspace/application/settings/date_time/date_format_ext.dart';
import 'package:appflowy/workspace/application/settings/date_time/time_format_ext.dart';
import 'package:appflowy/workspace/application/view/prelude.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_result/appflowy_result.dart';
import 'package:bloc/bloc.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:time/time.dart';
part 'notification_reminder_bloc.freezed.dart';
class NotificationReminderBloc
extends Bloc<NotificationReminderEvent, NotificationReminderState> {
NotificationReminderBloc() : super(NotificationReminderState.initial()) {
on<NotificationReminderEvent>((event, emit) async {
await event.when(
initial: (reminder, dateFormat, timeFormat) async {
this.reminder = reminder;
this.dateFormat = dateFormat;
this.timeFormat = timeFormat;
add(const NotificationReminderEvent.reset());
},
reset: () async {
final createdAt = await _getCreatedAt(
reminder,
dateFormat,
timeFormat,
);
final view = await _getView(reminder);
if (view == null) {
emit(
NotificationReminderState(
createdAt: createdAt,
pageTitle: '',
reminderContent: '',
status: NotificationReminderStatus.error,
),
);
}
final layout = view!.layout;
if (layout.isDocumentView) {
final node = await _getContent(reminder);
if (node != null) {
emit(
NotificationReminderState(
createdAt: createdAt,
pageTitle: view.name,
view: view,
reminderContent: node.delta?.toPlainText() ?? '',
nodes: [node],
status: NotificationReminderStatus.loaded,
),
);
}
} else if (layout.isDatabaseView) {
emit(
NotificationReminderState(
createdAt: createdAt,
pageTitle: view.name,
view: view,
reminderContent: reminder.message,
status: NotificationReminderStatus.loaded,
),
);
}
},
);
});
}
late final ReminderPB reminder;
late final UserDateFormatPB dateFormat;
late final UserTimeFormatPB timeFormat;
Future<String> _getCreatedAt(
ReminderPB reminder,
UserDateFormatPB dateFormat,
UserTimeFormatPB timeFormat,
) async {
final rCreatedAt = reminder.createdAt;
final createdAt = rCreatedAt != null
? _formatTimestamp(
rCreatedAt,
timeFormat: timeFormat,
dateFormate: dateFormat,
)
: '';
return createdAt;
}
Future<ViewPB?> _getView(ReminderPB reminder) async {
return ViewBackendService.getView(reminder.objectId)
.fold((s) => s, (_) => null);
}
Future<Node?> _getContent(ReminderPB reminder) async {
final blockId = reminder.meta[ReminderMetaKeys.blockId];
if (blockId == null) {
return null;
}
final document = await DocumentService()
.openDocument(
documentId: reminder.objectId,
)
.fold((s) => s.toDocument(), (_) => null);
if (document == null) {
return null;
}
final node = _searchById(document.root, blockId);
if (node == null) {
return null;
}
return node;
}
Node? _searchById(Node current, String id) {
if (current.id == id) {
return current;
}
if (current.children.isNotEmpty) {
for (final child in current.children) {
final node = _searchById(child, id);
if (node != null) {
return node;
}
}
}
return null;
}
String _formatTimestamp(
int timestamp, {
required UserDateFormatPB dateFormate,
required UserTimeFormatPB timeFormat,
}) {
final now = DateTime.now();
final dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp);
final difference = now.difference(dateTime);
final String date;
if (difference.inMinutes < 1) {
date = LocaleKeys.sideBar_justNow.tr();
} else if (difference.inHours < 1 && dateTime.isToday) {
// Less than 1 hour
date = LocaleKeys.sideBar_minutesAgo
.tr(namedArgs: {'count': difference.inMinutes.toString()});
} else if (difference.inHours >= 1 && dateTime.isToday) {
// in same day
date = timeFormat.formatTime(dateTime);
} else {
date = dateFormate.formatDate(dateTime, false);
}
return date;
}
}
@freezed
class NotificationReminderEvent with _$NotificationReminderEvent {
const factory NotificationReminderEvent.initial(
ReminderPB reminder,
UserDateFormatPB dateFormat,
UserTimeFormatPB timeFormat,
) = _Initial;
const factory NotificationReminderEvent.reset() = _Reset;
}
enum NotificationReminderStatus {
initial,
loading,
loaded,
error,
}
@freezed
class NotificationReminderState with _$NotificationReminderState {
const NotificationReminderState._();
const factory NotificationReminderState({
required String createdAt,
required String pageTitle,
required String reminderContent,
@Default(NotificationReminderStatus.initial)
NotificationReminderStatus status,
@Default([]) List<Node> nodes,
ViewPB? view,
}) = _NotificationReminderState;
factory NotificationReminderState.initial() =>
const NotificationReminderState(
createdAt: '',
pageTitle: '',
reminderContent: '',
);
}

View File

@ -23,6 +23,9 @@ class DocumentPageStyleBloc
await event.when(
initial: () async {
try {
if (view.id.isEmpty) {
return;
}
final layoutObject =
await ViewBackendService.getView(view.id).fold(
(s) => jsonDecode(s.extra),
@ -146,7 +149,7 @@ class DocumentPageStyleBloc
) {
double padding = switch (fontLayout) {
PageStyleFontLayout.small => 1.0,
PageStyleFontLayout.normal => 2.0,
PageStyleFontLayout.normal => 1.0,
PageStyleFontLayout.large => 4.0,
};
switch (lineHeightLayout) {
@ -162,6 +165,16 @@ class DocumentPageStyleBloc
return max(0, padding);
}
double calculateIconScale(
PageStyleFontLayout fontLayout,
) {
return switch (fontLayout) {
PageStyleFontLayout.small => 0.8,
PageStyleFontLayout.normal => 1.0,
PageStyleFontLayout.large => 1.2,
};
}
PageStyleFontLayout _getSelectedFontLayout(Map layoutObject) {
final fontLayout = layoutObject[ViewExtKeys.fontLayoutKey] ??
PageStyleFontLayout.normal.toString();

View File

@ -1,7 +1,5 @@
import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart';
import 'package:appflowy/plugins/document/application/document_data_pb_extension.dart';
import 'package:appflowy/plugins/document/application/document_listener.dart';
import 'package:appflowy/plugins/document/application/document_service.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy/workspace/application/view/prelude.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart';
@ -113,7 +111,6 @@ class RecentViewBloc extends Bloc<RecentViewEvent, RecentViewState> {
);
}
final _service = DocumentService();
final ViewPB view;
final DocumentListener _documentListener;
final ViewListener _viewListener;
@ -124,16 +121,6 @@ class RecentViewBloc extends Bloc<RecentViewEvent, RecentViewState> {
// for the version under 0.5.5
Future<(CoverType, String?)> getCoverV1() async {
final result = await _service.getDocument(documentId: view.id);
final document = result.fold((s) => s.toDocument(), (f) => null);
if (document != null) {
final coverType = CoverType.fromString(
document.root.attributes[DocumentHeaderBlockKeys.coverType],
);
final coverValue = document
.root.attributes[DocumentHeaderBlockKeys.coverDetails] as String?;
return (coverType, coverValue);
}
return (CoverType.none, null);
}

View File

@ -12,12 +12,12 @@ class UserProfileBloc extends Bloc<UserProfileEvent, UserProfileState> {
UserProfileBloc() : super(const _Initial()) {
on<UserProfileEvent>((event, emit) async {
await event.when(
started: () async => _initalize(emit),
started: () async => _initialize(emit),
);
});
}
Future<void> _initalize(Emitter<UserProfileState> emit) async {
Future<void> _initialize(Emitter<UserProfileState> emit) async {
emit(const UserProfileState.loading());
final workspaceOrFailure =

View File

@ -0,0 +1,54 @@
import 'package:appflowy/shared/feedback_gesture_detector.dart';
import 'package:flutter/material.dart';
class AnimatedGestureDetector extends StatefulWidget {
const AnimatedGestureDetector({
super.key,
this.scaleFactor = 0.98,
this.feedback = true,
this.duration = const Duration(milliseconds: 100),
this.alignment = Alignment.center,
this.behavior = HitTestBehavior.opaque,
this.onTapUp,
required this.child,
});
final Widget child;
final double scaleFactor;
final Duration duration;
final Alignment alignment;
final bool feedback;
final HitTestBehavior behavior;
final VoidCallback? onTapUp;
@override
State<AnimatedGestureDetector> createState() =>
_AnimatedGestureDetectorState();
}
class _AnimatedGestureDetectorState extends State<AnimatedGestureDetector> {
double scale = 1.0;
@override
Widget build(BuildContext context) {
return GestureDetector(
behavior: widget.behavior,
onTapUp: (details) {
setState(() => scale = 1.0);
HapticFeedbackType.light.call();
widget.onTapUp?.call();
},
onTapDown: (details) {
setState(() => scale = widget.scaleFactor);
},
child: AnimatedScale(
scale: scale,
alignment: widget.alignment,
duration: widget.duration,
child: widget.child,
),
);
}
}

View File

@ -10,7 +10,7 @@ enum FlowyAppBarLeadingType {
Widget getWidget(VoidCallback? onTap) {
switch (this) {
case FlowyAppBarLeadingType.back:
return AppBarBackButton(onTap: onTap);
return AppBarImmersiveBackButton(onTap: onTap);
case FlowyAppBarLeadingType.close:
return AppBarCloseButton(onTap: onTap);
case FlowyAppBarLeadingType.cancel:

View File

@ -26,6 +26,31 @@ class AppBarBackButton extends StatelessWidget {
}
}
class AppBarImmersiveBackButton extends StatelessWidget {
const AppBarImmersiveBackButton({
super.key,
this.onTap,
});
final VoidCallback? onTap;
@override
Widget build(BuildContext context) {
return AppBarButton(
onTap: (_) => (onTap ?? () => Navigator.pop(context)).call(),
padding: const EdgeInsets.only(
left: 12.0,
top: 8.0,
bottom: 8.0,
right: 4.0,
),
child: const FlowySvg(
FlowySvgs.m_app_bar_back_s,
),
);
}
}
class AppBarCloseButton extends StatelessWidget {
const AppBarCloseButton({
super.key,

View File

@ -3,8 +3,8 @@ import 'package:appflowy/mobile/application/base/mobile_view_page_bloc.dart';
import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart';
import 'package:appflowy/mobile/presentation/base/app_bar/app_bar.dart';
import 'package:appflowy/mobile/presentation/base/view_page/app_bar_buttons.dart';
import 'package:appflowy/mobile/presentation/presentation.dart';
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_state_container.dart';
import 'package:appflowy/plugins/base/emoji/emoji_text.dart';
import 'package:appflowy/plugins/document/presentation/document_collaborators.dart';
import 'package:appflowy/shared/feature_flags.dart';
import 'package:appflowy/startup/plugin/plugin.dart';
@ -27,6 +27,8 @@ class MobileViewPage extends StatefulWidget {
required this.viewLayout,
this.title,
this.arguments,
this.fixedTitle,
this.showMoreButton = true,
});
/// view id
@ -34,6 +36,10 @@ class MobileViewPage extends StatefulWidget {
final ViewLayoutPB viewLayout;
final String? title;
final Map<String, dynamic>? arguments;
final bool showMoreButton;
// only used in row page
final String? fixedTitle;
@override
State<MobileViewPage> createState() => _MobileViewPageState();
@ -46,10 +52,18 @@ class _MobileViewPageState extends State<MobileViewPage> {
// control the app bar opacity when in immersive mode
final ValueNotifier<double> _appBarOpacity = ValueNotifier(1.0);
@override
void initState() {
super.initState();
getIt<ReminderBloc>().add(const ReminderEvent.started());
}
@override
void dispose() {
_appBarOpacity.dispose();
_scrollNotificationObserver = null;
super.dispose();
}
@ -78,8 +92,7 @@ class _MobileViewPageState extends State<MobileViewPage> {
ViewBloc(view: view)..add(const ViewEvent.initial()),
),
BlocProvider.value(
value: getIt<ReminderBloc>()
..add(const ReminderEvent.started()),
value: getIt<ReminderBloc>(),
),
if (view.layout.isDocumentView)
BlocProvider(
@ -125,7 +138,7 @@ class _MobileViewPageState extends State<MobileViewPage> {
return child;
},
)
: child;
: SafeArea(child: child);
return Scaffold(
extendBodyBehindAppBar: isDocument,
appBar: appBar,
@ -157,6 +170,9 @@ class _MobileViewPageState extends State<MobileViewPage> {
return plugin.widgetBuilder.buildWidget(
shrinkWrap: false,
context: PluginContext(userProfile: state.userProfilePB),
data: {
MobileDocumentScreen.viewFixedTitle: widget.fixedTitle,
},
);
},
(error) {
@ -209,13 +225,19 @@ class _MobileViewPageState extends State<MobileViewPage> {
]);
}
actions.addAll([
MobileViewPageMoreButton(
view: view,
isImmersiveMode: isImmersiveMode,
appBarOpacity: _appBarOpacity,
),
]);
if (widget.showMoreButton) {
actions.addAll([
MobileViewPageMoreButton(
view: view,
isImmersiveMode: isImmersiveMode,
appBarOpacity: _appBarOpacity,
),
]);
} else {
actions.addAll([
const HSpace(18.0),
]);
}
return actions;
}
@ -225,19 +247,20 @@ class _MobileViewPageState extends State<MobileViewPage> {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
if (icon != null && icon.isNotEmpty)
ConstrainedBox(
constraints: const BoxConstraints.tightFor(width: 34.0),
child: EmojiText(
emoji: '$icon ',
fontSize: 22.0,
),
if (icon != null && icon.isNotEmpty) ...[
FlowyText.emoji(
icon,
fontSize: 15.0,
figmaLineHeight: 18.0,
),
const HSpace(4),
],
Expanded(
child: FlowyText.medium(
view?.name ?? widget.title ?? '',
widget.fixedTitle ?? view?.name ?? widget.title ?? '',
fontSize: 15.0,
overflow: TextOverflow.ellipsis,
figmaLineHeight: 18.0,
),
),
],

View File

@ -102,6 +102,7 @@ class _TypeOptionMenuItem<T> extends StatelessWidget {
value.text,
fontSize: 14.0,
maxLines: 2,
lineHeight: 1.0,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
),

View File

@ -24,9 +24,10 @@ class AddNewPageWidgetBottomSheet extends StatelessWidget {
height: 52.0,
leftIcon: const FlowySvg(
FlowySvgs.icon_document_s,
size: Size.square(18),
size: Size.square(20),
),
showTopBorder: false,
showBottomBorder: false,
onTap: () => onAction(ViewLayoutPB.Document),
),
FlowyOptionTile.text(
@ -34,9 +35,10 @@ class AddNewPageWidgetBottomSheet extends StatelessWidget {
height: 52.0,
leftIcon: const FlowySvg(
FlowySvgs.icon_grid_s,
size: Size.square(18),
size: Size.square(20),
),
showTopBorder: false,
showBottomBorder: false,
onTap: () => onAction(ViewLayoutPB.Grid),
),
FlowyOptionTile.text(
@ -44,9 +46,10 @@ class AddNewPageWidgetBottomSheet extends StatelessWidget {
height: 52.0,
leftIcon: const FlowySvg(
FlowySvgs.icon_board_s,
size: Size.square(18),
size: Size.square(20),
),
showTopBorder: false,
showBottomBorder: false,
onTap: () => onAction(ViewLayoutPB.Board),
),
FlowyOptionTile.text(
@ -54,9 +57,10 @@ class AddNewPageWidgetBottomSheet extends StatelessWidget {
height: 52.0,
leftIcon: const FlowySvg(
FlowySvgs.icon_calendar_s,
size: Size.square(18),
size: Size.square(20),
),
showTopBorder: false,
showBottomBorder: false,
onTap: () => onAction(ViewLayoutPB.Calendar),
),
FlowyOptionTile.text(
@ -64,9 +68,10 @@ class AddNewPageWidgetBottomSheet extends StatelessWidget {
height: 52.0,
leftIcon: const FlowySvg(
FlowySvgs.chat_ai_page_s,
size: Size.square(18),
size: Size.square(20),
),
showTopBorder: false,
showBottomBorder: false,
onTap: () => onAction(ViewLayoutPB.Chat),
),
],

View File

@ -52,6 +52,7 @@ class _MobileBottomSheetRenameWidgetState
height: 42.0,
child: FlowyTextField(
controller: controller,
textStyle: Theme.of(context).textTheme.bodyMedium,
keyboardType: TextInputType.text,
onSubmitted: (text) => widget.onRename(text),
),

View File

@ -1,4 +1,3 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
import 'package:appflowy/mobile/presentation/widgets/show_flowy_mobile_confirm_dialog.dart';
@ -6,6 +5,7 @@ import 'package:appflowy/startup/tasks/app_widget.dart';
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
import 'package:appflowy/workspace/application/recent/recent_views_bloc.dart';
import 'package:appflowy/workspace/application/view/view_bloc.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@ -64,6 +64,10 @@ class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
case MobileViewItemBottomSheetBodyAction.duplicate:
Navigator.pop(context);
context.read<ViewBloc>().add(const ViewEvent.duplicate());
showToastNotification(
context,
message: LocaleKeys.button_duplicateSuccessfully.tr(),
);
break;
case MobileViewItemBottomSheetBodyAction.share:
// unimplemented
@ -79,6 +83,12 @@ class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
context
.read<FavoriteBloc>()
.add(FavoriteEvent.toggle(widget.view));
showToastNotification(
context,
message: !widget.view.isFavorite
? LocaleKeys.button_favoriteSuccessfully.tr()
: LocaleKeys.button_unfavoriteSuccessfully.tr(),
);
break;
case MobileViewItemBottomSheetBodyAction.removeFromRecent:
_removeFromRecent(context);
@ -109,16 +119,6 @@ class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
await _showConfirmDialog(
onDelete: () {
recentViewsBloc.add(RecentViewsEvent.removeRecentViews([viewId]));
fToast.showToast(
child: const _RemoveToast(),
positionedToastBuilder: (context, child) {
return Positioned.fill(
top: 450,
child: child,
);
},
);
},
);
}
@ -126,48 +126,30 @@ class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
Future<void> _showConfirmDialog({required VoidCallback onDelete}) async {
await showFlowyCupertinoConfirmDialog(
title: LocaleKeys.sideBar_removePageFromRecent.tr(),
leftButton: FlowyText.regular(
leftButton: FlowyText(
LocaleKeys.button_cancel.tr(),
color: const Color(0xFF1456F0),
fontSize: 17.0,
figmaLineHeight: 24.0,
fontWeight: FontWeight.w500,
color: const Color(0xFF007AFF),
),
rightButton: FlowyText.medium(
rightButton: FlowyText(
LocaleKeys.button_delete.tr(),
fontSize: 17.0,
figmaLineHeight: 24.0,
fontWeight: FontWeight.w400,
color: const Color(0xFFFE0220),
),
onRightButtonPressed: (context) {
onDelete();
Navigator.pop(context);
showToastNotification(
context,
message: LocaleKeys.sideBar_removeSuccess.tr(),
);
},
);
}
}
class _RemoveToast extends StatelessWidget {
const _RemoveToast();
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 13.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
color: const Color(0xE5171717),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const FlowySvg(
FlowySvgs.success_s,
blendMode: null,
),
const HSpace(8.0),
FlowyText.regular(
LocaleKeys.sideBar_removeSuccess.tr(),
fontSize: 16.0,
color: Colors.white,
),
],
),
);
}
}

View File

@ -7,6 +7,7 @@ import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
import 'package:appflowy/workspace/application/recent/recent_views_bloc.dart';
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
import 'package:appflowy/workspace/application/view/view_bloc.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@ -40,18 +41,32 @@ enum MobilePaneActionType {
backgroundColor: const Color(0xFFFA217F),
svg: FlowySvgs.favorite_section_remove_from_favorite_s,
size: 24.0,
onPressed: (context) => context
.read<FavoriteBloc>()
.add(FavoriteEvent.toggle(context.read<ViewBloc>().view)),
onPressed: (context) {
showToastNotification(
context,
message: LocaleKeys.button_unfavoriteSuccessfully.tr(),
);
context
.read<FavoriteBloc>()
.add(FavoriteEvent.toggle(context.read<ViewBloc>().view));
},
);
case MobilePaneActionType.addToFavorites:
return MobileSlideActionButton(
backgroundColor: const Color(0xFF00C8FF),
svg: FlowySvgs.favorite_s,
size: 24.0,
onPressed: (context) => context
.read<FavoriteBloc>()
.add(FavoriteEvent.toggle(context.read<ViewBloc>().view)),
onPressed: (context) {
showToastNotification(
context,
message: LocaleKeys.button_favoriteSuccessfully.tr(),
);
context
.read<FavoriteBloc>()
.add(FavoriteEvent.toggle(context.read<ViewBloc>().view));
},
);
case MobilePaneActionType.add:
return MobileSlideActionButton(
@ -69,6 +84,7 @@ enum MobilePaneActionType {
showDragHandle: true,
showCloseButton: true,
useRootNavigator: true,
showDivider: false,
backgroundColor: Theme.of(context).colorScheme.surface,
builder: (sheetContext) {
return AddNewPageWidgetBottomSheet(
@ -145,8 +161,6 @@ enum MobilePaneActionType {
? MobileViewItemBottomSheetBodyAction.removeFromFavorites
: MobileViewItemBottomSheetBodyAction.addToFavorites,
MobileViewItemBottomSheetBodyAction.divider,
if (view.layout != ViewLayoutPB.Chat)
MobileViewItemBottomSheetBodyAction.duplicate,
MobileViewItemBottomSheetBodyAction.divider,
MobileViewItemBottomSheetBodyAction.removeFromRecent,
];
@ -156,7 +170,6 @@ enum MobilePaneActionType {
? MobileViewItemBottomSheetBodyAction.removeFromFavorites
: MobileViewItemBottomSheetBodyAction.addToFavorites,
MobileViewItemBottomSheetBodyAction.divider,
MobileViewItemBottomSheetBodyAction.duplicate,
];
}
}
@ -181,12 +194,13 @@ ActionPane buildEndActionPane(
bool needSpace = true,
MobilePageCardType? cardType,
FolderSpaceType? spaceType,
required double spaceRatio,
}) {
return ActionPane(
motion: const ScrollMotion(),
extentRatio: actions.length / 5,
extentRatio: actions.length / spaceRatio,
children: [
if (needSpace) const HSpace(20),
if (needSpace) const HSpace(60),
...actions.map(
(action) => action.actionButton(
context,

View File

@ -70,6 +70,7 @@ Future<T?> showMobileBottomSheet<T>(
backgroundColor ??= Theme.of(context).brightness == Brightness.light
? const Color(0xFFF7F8FB)
: const Color(0xFF23262B);
barrierColor ??= Colors.black.withOpacity(0.3);
return showModalBottomSheet<T>(
context: context,
@ -226,10 +227,14 @@ class BottomSheetHeader extends StatelessWidget {
),
),
Align(
child: FlowyText(
title,
fontSize: 16.0,
fontWeight: FontWeight.w500,
child: Container(
constraints: const BoxConstraints(maxWidth: 250),
child: FlowyText(
title,
fontSize: 17.0,
fontWeight: FontWeight.w500,
overflow: TextOverflow.ellipsis,
),
),
),
if (showDoneButton)

View File

@ -3,13 +3,13 @@ import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/database/board/board.dart';
import 'package:appflowy/mobile/presentation/database/board/widgets/group_card_header.dart';
import 'package:appflowy/mobile/presentation/database/card/card.dart';
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
import 'package:appflowy/plugins/database/application/database_controller.dart';
import 'package:appflowy/plugins/database/board/application/board_bloc.dart';
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/field_type_extension.dart';
import 'package:appflowy/plugins/database/widgets/card/card.dart';
import 'package:appflowy/plugins/database/widgets/cell/card_cell_builder.dart';
import 'package:appflowy/plugins/database/widgets/cell/card_cell_style_maps/mobile_board_card_cell_style.dart';
import 'package:appflowy/shared/flowy_error_page.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
@ -69,10 +69,8 @@ class _MobileBoardPageState extends State<MobileBoardPage> {
loading: (_) => const Center(
child: CircularProgressIndicator.adaptive(),
),
error: (err) => FlowyMobileStateContainer.error(
emoji: '🛸',
title: LocaleKeys.board_mobile_failedToLoad.tr(),
errorMsg: err.toString(),
error: (err) => AppFlowyErrorPage(
error: err.error,
),
ready: (data) => const _BoardContent(),
orElse: () => const SizedBox.shrink(),

View File

@ -3,6 +3,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/base/app_bar/app_bar.dart';
import 'package:appflowy/mobile/presentation/base/app_bar/app_bar_actions.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
import 'package:appflowy/mobile/presentation/database/card/card_detail/widgets/row_page_button.dart';
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_quick_action_button.dart';
import 'package:appflowy/plugins/database/application/cell/bloc/text_cell_bloc.dart';
import 'package:appflowy/plugins/database/application/cell/cell_controller.dart';
@ -294,6 +295,7 @@ class MobileRowDetailPageContentState
RowCache get rowCache => widget.databaseController.rowCache;
FieldController get fieldController =>
widget.databaseController.fieldController;
ValueNotifier<String> primaryFieldId = ValueNotifier('');
@override
void initState() {
@ -304,6 +306,8 @@ class MobileRowDetailPageContentState
viewId: viewId,
rowCache: rowCache,
);
rowController.initialize();
cellBuilder = EditableCellBuilder(
databaseController: widget.databaseController,
);
@ -326,7 +330,13 @@ class MobileRowDetailPageContentState
fieldController: fieldController,
rowMeta: rowController.rowMeta,
)..add(const RowBannerEvent.initial()),
child: BlocBuilder<RowBannerBloc, RowBannerState>(
child: BlocConsumer<RowBannerBloc, RowBannerState>(
listener: (context, state) {
if (state.primaryField == null) {
return;
}
primaryFieldId.value = state.primaryField!.id;
},
builder: (context, state) {
if (state.primaryField == null) {
return const SizedBox.shrink();
@ -366,6 +376,23 @@ class MobileRowDetailPageContentState
if (rowDetailState.numHiddenFields != 0) ...[
const ToggleHiddenFieldsVisibilityButton(),
],
const VSpace(8.0),
ValueListenableBuilder(
valueListenable: primaryFieldId,
builder: (context, primaryFieldId, child) {
if (primaryFieldId.isEmpty) {
return const SizedBox.shrink();
}
return OpenRowPageButton(
databaseController: widget.databaseController,
cellContext: CellContext(
rowId: rowController.rowId,
fieldId: primaryFieldId,
),
documentId: rowController.rowMeta.documentId,
);
},
),
MobileRowDetailCreateFieldButton(
viewId: viewId,
fieldController: fieldController,

View File

@ -22,7 +22,7 @@ class MobileRowDetailCreateFieldButton extends StatelessWidget {
return ConstrainedBox(
constraints: BoxConstraints(
minWidth: double.infinity,
minHeight: GridSize.headerHeight,
maxHeight: GridSize.headerHeight,
),
child: TextButton.icon(
style: Theme.of(context).textButtonTheme.style?.copyWith(
@ -37,7 +37,7 @@ class MobileRowDetailCreateFieldButton extends StatelessWidget {
alignment: AlignmentDirectional.centerStart,
splashFactory: NoSplash.splashFactory,
padding: const WidgetStatePropertyAll(
EdgeInsets.symmetric(vertical: 14, horizontal: 6),
EdgeInsets.symmetric(horizontal: 6, vertical: 2),
),
),
label: FlowyText.medium(

View File

@ -87,6 +87,7 @@ class _PropertyCellState extends State<_PropertyCell> {
fieldInfo.name,
overflow: TextOverflow.ellipsis,
fontSize: 14,
figmaLineHeight: 16.0,
color: Theme.of(context).hintColor,
),
),

Some files were not shown because too many files have changed in this diff Show More