diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/OFL.txt b/frontend/appflowy_tauri/public/google_fonts/Poppins/OFL.txt new file mode 100644 index 0000000000..246c977c9f --- /dev/null +++ b/frontend/appflowy_tauri/public/google_fonts/Poppins/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Black.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Black.ttf new file mode 100644 index 0000000000..71c0f995ee Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Black.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-BlackItalic.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-BlackItalic.ttf new file mode 100644 index 0000000000..7aeb58bd1b Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-BlackItalic.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Bold.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Bold.ttf new file mode 100644 index 0000000000..00559eeb29 Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Bold.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-BoldItalic.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-BoldItalic.ttf new file mode 100644 index 0000000000..e61e8e88bd Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-BoldItalic.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraBold.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraBold.ttf new file mode 100644 index 0000000000..df7093608a Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraBold.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraBoldItalic.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraBoldItalic.ttf new file mode 100644 index 0000000000..14d2b375dc Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraBoldItalic.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraLight.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraLight.ttf new file mode 100644 index 0000000000..e76ec69a65 Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraLight.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraLightItalic.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraLightItalic.ttf new file mode 100644 index 0000000000..89513d9469 Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraLightItalic.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Italic.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Italic.ttf new file mode 100644 index 0000000000..12b7b3c40b Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Italic.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Light.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Light.ttf new file mode 100644 index 0000000000..bc36bcc242 Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Light.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-LightItalic.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-LightItalic.ttf new file mode 100644 index 0000000000..9e70be6a9e Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-LightItalic.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Medium.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Medium.ttf new file mode 100644 index 0000000000..6bcdcc27f2 Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Medium.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-MediumItalic.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-MediumItalic.ttf new file mode 100644 index 0000000000..be67410fd0 Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-MediumItalic.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Regular.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Regular.ttf new file mode 100644 index 0000000000..9f0c71b70a Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Regular.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-SemiBold.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-SemiBold.ttf new file mode 100644 index 0000000000..74c726e327 Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-SemiBold.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-SemiBoldItalic.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-SemiBoldItalic.ttf new file mode 100644 index 0000000000..3e6c942233 Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-SemiBoldItalic.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Thin.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Thin.ttf new file mode 100644 index 0000000000..03e736613a Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Thin.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ThinItalic.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ThinItalic.ttf new file mode 100644 index 0000000000..e26db5dd3d Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ThinItalic.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Roboto_Mono/LICENSE.txt b/frontend/appflowy_tauri/public/google_fonts/Roboto_Mono/LICENSE.txt new file mode 100644 index 0000000000..75b52484ea --- /dev/null +++ b/frontend/appflowy_tauri/public/google_fonts/Roboto_Mono/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/frontend/appflowy_tauri/public/google_fonts/Roboto_Mono/RobotoMono-Italic.ttf b/frontend/appflowy_tauri/public/google_fonts/Roboto_Mono/RobotoMono-Italic.ttf new file mode 100644 index 0000000000..61e5303325 Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Roboto_Mono/RobotoMono-Italic.ttf differ diff --git a/frontend/appflowy_tauri/public/google_fonts/Roboto_Mono/RobotoMono-Regular.ttf b/frontend/appflowy_tauri/public/google_fonts/Roboto_Mono/RobotoMono-Regular.ttf new file mode 100644 index 0000000000..6df2b25360 Binary files /dev/null and b/frontend/appflowy_tauri/public/google_fonts/Roboto_Mono/RobotoMono-Regular.ttf differ diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/launch_splash.jpg b/frontend/appflowy_tauri/public/launch_splash.jpg similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/assets/launch_splash.jpg rename to frontend/appflowy_tauri/public/launch_splash.jpg diff --git a/frontend/appflowy_tauri/src-tauri/src/main.rs b/frontend/appflowy_tauri/src-tauri/src/main.rs index 111de889f1..10a17b5a3a 100644 --- a/frontend/appflowy_tauri/src-tauri/src/main.rs +++ b/frontend/appflowy_tauri/src-tauri/src/main.rs @@ -31,11 +31,20 @@ fn main() { }); }) .setup(|_app| { - #[cfg(debug_assertions)] - { - let window = _app.get_window("main").unwrap(); - window.open_devtools(); - } + let splashscreen_window = _app.get_window("splashscreen").unwrap(); + let window = _app.get_window("main").unwrap(); + + // we perform the initialization code on a new task so the app doesn't freeze + tauri::async_runtime::spawn(async move { + // initialize your app here instead of sleeping :) + println!("Initializing..."); + std::thread::sleep(std::time::Duration::from_secs(2)); + println!("Done initializing."); + + // After it's done, close the splashscreen and display the main window + splashscreen_window.close().unwrap(); + window.show().unwrap(); + }); Ok(()) }) .run(tauri::generate_context!()) diff --git a/frontend/appflowy_tauri/src-tauri/tauri.conf.json b/frontend/appflowy_tauri/src-tauri/tauri.conf.json index 293da0ec70..5011422312 100644 --- a/frontend/appflowy_tauri/src-tauri/tauri.conf.json +++ b/frontend/appflowy_tauri/src-tauri/tauri.conf.json @@ -95,7 +95,18 @@ "title": "AppFlowy", "width": 1200, "minWidth": 800, - "minHeight": 600 + "minHeight": 600, + "visible": false, + "label": "main" + }, + { + "height": 300, + "width": 549, + "decorations": false, + "url": "launch_splash.jpg", + "label": "splashscreen", + "center": true, + "visible": true } ] } diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/folder/workspace.service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/folder/workspace.service.ts index 0a1ac683af..e58afb9f58 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/application/folder/workspace.service.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/application/folder/workspace.service.ts @@ -1,5 +1,15 @@ -import { CreateViewPayloadPB, UserWorkspaceIdPB, WorkspaceIdPB } from '@/services/backend'; -import { UserEventOpenWorkspace } from '@/services/backend/events/flowy-user'; +import { + CreateViewPayloadPB, + UserWorkspaceIdPB, + WorkspaceIdPB, + RenameWorkspacePB, + ChangeWorkspaceIconPB, +} from '@/services/backend'; +import { + UserEventOpenWorkspace, + UserEventRenameWorkspace, + UserEventChangeWorkspaceIcon, +} from '@/services/backend/events/flowy-user'; import { FolderEventCreateView, FolderEventDeleteWorkspace, @@ -108,3 +118,33 @@ export async function createCurrentWorkspaceChildView( return Promise.reject(result.err); } + +export async function renameWorkspace(id: string, name: string) { + const payload = new RenameWorkspacePB({ + workspace_id: id, + new_name: name, + }); + + const result = await UserEventRenameWorkspace(payload); + + if (result.ok) { + return result.val; + } + + return Promise.reject(result.err); +} + +export async function changeWorkspaceIcon(id: string, icon: string) { + const payload = new ChangeWorkspaceIconPB({ + workspace_id: id, + new_icon: icon, + }); + + const result = await UserEventChangeWorkspaceIcon(payload); + + if (result.ok) { + return result.val; + } + + return Promise.reject(result.err); +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/notification.ts b/frontend/appflowy_tauri/src/appflowy_app/application/notification.ts index 91bb15f069..c63a5d9823 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/application/notification.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/application/notification.ts @@ -22,6 +22,7 @@ import { ViewPB, RepeatedTrashPB, ChildViewUpdatePB, + WorkspacePB, } from '@/services/backend'; import { AsyncQueue } from '$app/utils/async_queue'; @@ -40,11 +41,12 @@ const Notification = { [DatabaseNotification.DidUpdateFieldSettings]: FieldSettingsPB, [DatabaseNotification.DidUpdateFilter]: FilterChangesetNotificationPB, [DocumentNotification.DidReceiveUpdate]: DocEventPB, - [UserNotification.DidUpdateUserProfile]: UserProfilePB, + [FolderNotification.DidUpdateWorkspace]: WorkspacePB, [FolderNotification.DidUpdateWorkspaceViews]: RepeatedViewPB, [FolderNotification.DidUpdateView]: ViewPB, [FolderNotification.DidUpdateChildViews]: ChildViewUpdatePB, [FolderNotification.DidUpdateTrash]: RepeatedTrashPB, + [UserNotification.DidUpdateUserProfile]: UserProfilePB, }; type NotificationMap = typeof Notification; @@ -106,7 +108,7 @@ export function subscribeNotifications( callbacks: { [K in NotificationEnum]?: NotificationHandler; }, - options?: { id?: string } + options?: { id?: string | number } ): Promise<() => void> { const handler = async (subject: SubscribeObject) => { const { id, ty } = subject; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/user/user.service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/user/user.service.ts index f91c39cb71..ec64fb810c 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/application/user/user.service.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/application/user/user.service.ts @@ -1,9 +1,10 @@ import { Theme, ThemeMode, UserSetting } from '$app_reducers/current-user/slice'; -import { AppearanceSettingsPB } from '@/services/backend'; +import { AppearanceSettingsPB, UpdateUserProfilePayloadPB } from '@/services/backend'; import { UserEventGetAppearanceSetting, UserEventGetUserProfile, UserEventSetAppearanceSetting, + UserEventUpdateUserProfile, } from '@/services/backend/events/flowy-user'; export const UserService = { @@ -52,4 +53,16 @@ export const UserService = { return; }, + + updateUserProfile: async (params: ReturnType) => { + const payload = UpdateUserProfilePayloadPB.fromObject(params); + + const res = await UserEventUpdateUserProfile(payload); + + if (res.ok) { + return res.val; + } + + return Promise.reject(res.err); + }, }; diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/dark-logo.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/dark-logo.svg new file mode 100644 index 0000000000..80d8c4132e --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/assets/dark-logo.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/information.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/information.svg new file mode 100644 index 0000000000..37ca4d5837 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/assets/information.svg @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/light-logo.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/light-logo.svg new file mode 100644 index 0000000000..f5cd761ba7 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/assets/light-logo.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/logo.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/logo.svg new file mode 100644 index 0000000000..b1ac8d66fb --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/assets/logo.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/select-check.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/select-check.svg index 4e4a8c039a..05caec861a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/select-check.svg +++ b/frontend/appflowy_tauri/src/appflowy_app/assets/select-check.svg @@ -1,3 +1,3 @@ - + diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/settings/account.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/account.svg new file mode 100644 index 0000000000..fddfca7575 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/account.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/settings/check_circle.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/check_circle.svg new file mode 100644 index 0000000000..c6fa56067b --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/check_circle.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/settings/dark.png b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/dark.png new file mode 100644 index 0000000000..15a2db5eb8 Binary files /dev/null and b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/dark.png differ diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/settings/discord.png b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/discord.png new file mode 100644 index 0000000000..f71e68c6ed Binary files /dev/null and b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/discord.png differ diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/settings/github.png b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/github.png new file mode 100644 index 0000000000..597883b7a3 Binary files /dev/null and b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/github.png differ diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/settings/google.png b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/google.png new file mode 100644 index 0000000000..60032628a8 Binary files /dev/null and b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/google.png differ diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/settings/light.png b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/light.png new file mode 100644 index 0000000000..09b2d9c475 Binary files /dev/null and b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/light.png differ diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/settings/workplace.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/workplace.svg new file mode 100644 index 0000000000..2076ea3e2c --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/workplace.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/ProfileAvatar.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/ProfileAvatar.tsx new file mode 100644 index 0000000000..1248882238 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/ProfileAvatar.tsx @@ -0,0 +1,33 @@ +import { stringToColor, stringToShortName } from '$app/utils/avatar'; +import { Avatar } from '@mui/material'; +import { useAppSelector } from '$app/stores/store'; + +export const ProfileAvatar = ({ + onClick, + className, + width, + height, +}: { + onClick?: (e: React.MouseEvent) => void; + width?: number; + height?: number; + className?: string; +}) => { + const { displayName = 'Me', iconUrl } = useAppSelector((state) => state.currentUser); + + return ( + + {iconUrl ? iconUrl : stringToShortName(displayName)} + + ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/WorkplaceAvatar.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/WorkplaceAvatar.tsx new file mode 100644 index 0000000000..079342b528 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/WorkplaceAvatar.tsx @@ -0,0 +1,34 @@ +import { Avatar } from '@mui/material'; +import { stringToColor, stringToShortName } from '$app/utils/avatar'; + +export const WorkplaceAvatar = ({ + workplaceName, + icon, + onClick, + width, + height, + className, +}: { + workplaceName: string; + width: number; + height: number; + className?: string; + icon?: string; + onClick?: (e: React.MouseEvent) => void; +}) => { + return ( + + {icon ? icon : stringToShortName(workplaceName)} + + ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/index.ts new file mode 100644 index 0000000000..772056737a --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/index.ts @@ -0,0 +1,2 @@ +export * from './WorkplaceAvatar'; +export * from './ProfileAvatar'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/login/LoginButtonGroup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/login/LoginButtonGroup.tsx new file mode 100644 index 0000000000..7334a94420 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/login/LoginButtonGroup.tsx @@ -0,0 +1,26 @@ +import Button from '@mui/material/Button'; +import GoogleIcon from '$app/assets/settings/google.png'; +import GithubIcon from '$app/assets/settings/github.png'; +import DiscordIcon from '$app/assets/settings/discord.png'; +import { useTranslation } from 'react-i18next'; + +export const LoginButtonGroup = () => { + const { t } = useTranslation(); + + return ( +
+ + + +
+ ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/login/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/login/index.ts new file mode 100644 index 0000000000..04605317ed --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/login/index.ts @@ -0,0 +1 @@ +export * from './LoginButtonGroup'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/AddSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/AddSvg.tsx deleted file mode 100644 index 495a0151c1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/AddSvg.tsx +++ /dev/null @@ -1,8 +0,0 @@ -export default () => { - return ( - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/AppflowyLogo.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/AppflowyLogo.tsx deleted file mode 100644 index 0625424c76..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/AppflowyLogo.tsx +++ /dev/null @@ -1,42 +0,0 @@ -export const AppflowyLogo = () => { - return ( - - - - - - - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/AppflowyLogoDark.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/AppflowyLogoDark.tsx deleted file mode 100644 index f43ce1f495..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/AppflowyLogoDark.tsx +++ /dev/null @@ -1,77 +0,0 @@ -export const AppflowyLogoDark = () => { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/AppflowyLogoLight.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/AppflowyLogoLight.tsx deleted file mode 100644 index 1c9b3dcbb2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/AppflowyLogoLight.tsx +++ /dev/null @@ -1,53 +0,0 @@ -export const AppflowyLogoLight = () => ( - - - - - - - - - - - - - - - - - - - -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ArrowLeftSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ArrowLeftSvg.tsx deleted file mode 100644 index 9c4d68be75..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ArrowLeftSvg.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export const ArrowLeftSvg = () => { - return ( - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ArrowRightSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ArrowRightSvg.tsx deleted file mode 100644 index 8b9501c508..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ArrowRightSvg.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export const ArrowRightSvg = () => { - return ( - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/BoardSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/BoardSvg.tsx deleted file mode 100644 index 11c29fae58..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/BoardSvg.tsx +++ /dev/null @@ -1,20 +0,0 @@ -export const BoardSvg = () => { - return ( - - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/CheckboxSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/CheckboxSvg.tsx deleted file mode 100644 index 862badd2a2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/CheckboxSvg.tsx +++ /dev/null @@ -1,13 +0,0 @@ -export const CheckboxSvg = () => { - return ( - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ChecklistTypeSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ChecklistTypeSvg.tsx deleted file mode 100644 index ea4f168737..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ChecklistTypeSvg.tsx +++ /dev/null @@ -1,13 +0,0 @@ -export const ChecklistTypeSvg = () => { - return ( - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/CheckmarkSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/CheckmarkSvg.tsx deleted file mode 100644 index 7ab64e7e28..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/CheckmarkSvg.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export const CheckmarkSvg = () => { - return ( - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ClockSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ClockSvg.tsx deleted file mode 100644 index b66f7bfe18..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ClockSvg.tsx +++ /dev/null @@ -1,15 +0,0 @@ -export const ClockSvg = () => { - return ( - - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/CloseSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/CloseSvg.tsx deleted file mode 100644 index 50e76a68c5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/CloseSvg.tsx +++ /dev/null @@ -1,16 +0,0 @@ -export const CloseSvg = () => { - return ( - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/CopySvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/CopySvg.tsx deleted file mode 100644 index 9d4eb5bfca..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/CopySvg.tsx +++ /dev/null @@ -1,18 +0,0 @@ -export const CopySvg = () => { - return ( - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/DateTypeSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/DateTypeSvg.tsx deleted file mode 100644 index 7bc133b2af..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/DateTypeSvg.tsx +++ /dev/null @@ -1,15 +0,0 @@ -export const DateTypeSvg = () => { - return ( - - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/Details2Svg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/Details2Svg.tsx deleted file mode 100644 index 47e8cd9a00..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/Details2Svg.tsx +++ /dev/null @@ -1,8 +0,0 @@ -export const Details2Svg = () => { - return ( - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/DocumentSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/DocumentSvg.tsx deleted file mode 100644 index 52843553d4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/DocumentSvg.tsx +++ /dev/null @@ -1,18 +0,0 @@ -export const DocumentSvg = () => { - return ( - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/DragElementSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/DragElementSvg.tsx deleted file mode 100644 index 9d58ccde0f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/DragElementSvg.tsx +++ /dev/null @@ -1,12 +0,0 @@ -export const DragElementSvg = () => { - return ( - - - - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/DragSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/DragSvg.tsx deleted file mode 100644 index cce8d09199..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/DragSvg.tsx +++ /dev/null @@ -1,12 +0,0 @@ -export const DragSvg = () => { - return ( - - - - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/DropDownShowSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/DropDownShowSvg.tsx deleted file mode 100644 index b3956a77d1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/DropDownShowSvg.tsx +++ /dev/null @@ -1,10 +0,0 @@ -export const DropDownShowSvg = () => { - return ( - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EarthSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EarthSvg.tsx deleted file mode 100644 index f2911a940c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EarthSvg.tsx +++ /dev/null @@ -1,21 +0,0 @@ -export const EarthSvg = () => { - return ( - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EditSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EditSvg.tsx deleted file mode 100644 index 7174c1c373..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EditSvg.tsx +++ /dev/null @@ -1,13 +0,0 @@ -export const EditSvg = () => { - return ( - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EditorCheckSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EditorCheckSvg.tsx deleted file mode 100644 index c784aa0be6..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EditorCheckSvg.tsx +++ /dev/null @@ -1,13 +0,0 @@ -export const EditorCheckSvg = () => { - return ( - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EditorUncheckSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EditorUncheckSvg.tsx deleted file mode 100644 index 3f62b51eac..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EditorUncheckSvg.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export const EditorUncheckSvg = () => { - return ( - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EyeClosedSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EyeClosedSvg.tsx deleted file mode 100644 index d4d50668d3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EyeClosedSvg.tsx +++ /dev/null @@ -1,13 +0,0 @@ -export const EyeClosedSvg = () => { - return ( - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EyeOpenSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EyeOpenSvg.tsx deleted file mode 100644 index c775d233cc..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/EyeOpenSvg.tsx +++ /dev/null @@ -1,20 +0,0 @@ -export const EyeOpenSvg = () => { - return ( - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/FilterSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/FilterSvg.tsx deleted file mode 100644 index d46600610a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/FilterSvg.tsx +++ /dev/null @@ -1,12 +0,0 @@ -export const FilterSvg = () => { - return ( - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/FullView.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/FullView.tsx deleted file mode 100644 index aa79420d5a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/FullView.tsx +++ /dev/null @@ -1,10 +0,0 @@ -export const FullView = () => { - return ( - - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/GridSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/GridSvg.tsx deleted file mode 100644 index 5fbf0d86d7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/GridSvg.tsx +++ /dev/null @@ -1,30 +0,0 @@ -export const GridSvg = () => { - return ( - - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/GroupByFieldSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/GroupByFieldSvg.tsx deleted file mode 100644 index 960e1bad2a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/GroupByFieldSvg.tsx +++ /dev/null @@ -1,26 +0,0 @@ -export const GroupByFieldSvg = () => { - return ( - - - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/GroupBySvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/GroupBySvg.tsx deleted file mode 100644 index 7ac7e37303..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/GroupBySvg.tsx +++ /dev/null @@ -1,31 +0,0 @@ -export const GroupBySvg = () => { - return ( - - - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/HideMenuSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/HideMenuSvg.tsx deleted file mode 100644 index af69b2ab5c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/HideMenuSvg.tsx +++ /dev/null @@ -1,10 +0,0 @@ -export const HideMenuSvg = () => { - return ( - - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ImageSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ImageSvg.tsx deleted file mode 100644 index 488c170656..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ImageSvg.tsx +++ /dev/null @@ -1,9 +0,0 @@ -export const ImageSvg = () => { - return ( - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/InformationSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/InformationSvg.tsx deleted file mode 100644 index 8217fe0f82..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/InformationSvg.tsx +++ /dev/null @@ -1,14 +0,0 @@ -export const InformationSvg = () => { - return ( - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/LogoutSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/LogoutSvg.tsx deleted file mode 100644 index 86e69c08c1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/LogoutSvg.tsx +++ /dev/null @@ -1,14 +0,0 @@ -export const LogoutSvg = () => { - return ( - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/MoreSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/MoreSvg.tsx deleted file mode 100644 index 20dd851302..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/MoreSvg.tsx +++ /dev/null @@ -1,10 +0,0 @@ -export const MoreSvg = () => { - return ( - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/MultiSelectTypeSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/MultiSelectTypeSvg.tsx deleted file mode 100644 index ec9c56d868..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/MultiSelectTypeSvg.tsx +++ /dev/null @@ -1,12 +0,0 @@ -export const MultiSelectTypeSvg = () => { - return ( - - - - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/NumberTypeSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/NumberTypeSvg.tsx deleted file mode 100644 index b41a8704e4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/NumberTypeSvg.tsx +++ /dev/null @@ -1,10 +0,0 @@ -export const NumberTypeSvg = () => { - return ( - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/PropertiesSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/PropertiesSvg.tsx deleted file mode 100644 index cef2527c72..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/PropertiesSvg.tsx +++ /dev/null @@ -1,18 +0,0 @@ -export const PropertiesSvg = () => { - return ( - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SearchSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SearchSvg.tsx deleted file mode 100644 index 28717c95a0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SearchSvg.tsx +++ /dev/null @@ -1,18 +0,0 @@ -export const SearchSvg = () => { - return ( - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SettingsSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SettingsSvg.tsx deleted file mode 100644 index 84f449bc27..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SettingsSvg.tsx +++ /dev/null @@ -1,12 +0,0 @@ -export const SettingsSvg = () => { - return ( - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ShareSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ShareSvg.tsx deleted file mode 100644 index 217a995f62..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ShareSvg.tsx +++ /dev/null @@ -1,18 +0,0 @@ -export const ShareSvg = () => { - return ( - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ShowMenuSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ShowMenuSvg.tsx deleted file mode 100644 index 736e9a8b50..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ShowMenuSvg.tsx +++ /dev/null @@ -1,10 +0,0 @@ -export const ShowMenuSvg = () => { - return ( - - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SingleSelectTypeSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SingleSelectTypeSvg.tsx deleted file mode 100644 index 82b847681d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SingleSelectTypeSvg.tsx +++ /dev/null @@ -1,16 +0,0 @@ -export const SingleSelectTypeSvg = () => { - return ( - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SkipLeftSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SkipLeftSvg.tsx deleted file mode 100644 index 4e84c77e06..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SkipLeftSvg.tsx +++ /dev/null @@ -1,9 +0,0 @@ -export const SkipLeftSvg = () => { - return ( - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SkipRightSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SkipRightSvg.tsx deleted file mode 100644 index 6bcf2ebe7e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SkipRightSvg.tsx +++ /dev/null @@ -1,9 +0,0 @@ -export const SkipRightSvg = () => { - return ( - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SortAscSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SortAscSvg.tsx deleted file mode 100644 index 7304ef7cbb..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SortAscSvg.tsx +++ /dev/null @@ -1,22 +0,0 @@ -export const SortAscSvg = () => { - return ( - - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SortDescSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SortDescSvg.tsx deleted file mode 100644 index c0d310b6de..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SortDescSvg.tsx +++ /dev/null @@ -1,22 +0,0 @@ -export const SortDescSvg = () => { - return ( - - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SortSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SortSvg.tsx deleted file mode 100644 index 7fb7d9564f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/SortSvg.tsx +++ /dev/null @@ -1,8 +0,0 @@ -export const SortSvg = () => { - return ( - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/TextTypeSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/TextTypeSvg.tsx deleted file mode 100644 index 5ace944b18..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/TextTypeSvg.tsx +++ /dev/null @@ -1,14 +0,0 @@ -export const TextTypeSvg = () => { - return ( - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/TrashSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/TrashSvg.tsx deleted file mode 100644 index cb445e4704..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/TrashSvg.tsx +++ /dev/null @@ -1,34 +0,0 @@ -export const TrashSvg = () => { - return ( - - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/UrlTypeSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/UrlTypeSvg.tsx deleted file mode 100644 index 0c7a268ec3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/UrlTypeSvg.tsx +++ /dev/null @@ -1,13 +0,0 @@ -export const UrlTypeSvg = () => { - return ( - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewBanner.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewBanner.tsx index 2888c07231..e815597456 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewBanner.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewBanner.tsx @@ -19,10 +19,10 @@ function ViewBanner({ onUpdateCover?: (cover?: PageCover) => void; }) { return ( -
+
{showCover && cover && } -
+
-
- - +
); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/ProtectedRoutes.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/auth/ProtectedRoutes.tsx index 341eff871e..0d776bada5 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/ProtectedRoutes.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/auth/ProtectedRoutes.tsx @@ -2,11 +2,11 @@ import { Outlet } from 'react-router-dom'; import { useAuth } from './auth.hooks'; import Layout from '$app/components/layout/Layout'; import { useCallback, useEffect, useState } from 'react'; -import { GetStarted } from '$app/components/auth/get_started/GetStarted'; -import { AppflowyLogo } from '../_shared/svg/AppflowyLogo'; +import { Welcome } from '$app/components/auth/Welcome'; +import { ReactComponent as AppflowyLogo } from '$app/assets/logo.svg'; export const ProtectedRoutes = () => { - const { currentUser, checkUser } = useAuth(); + const { currentUser, checkUser, subscribeToUser } = useAuth(); const [isLoading, setIsLoading] = useState(true); const checkUserStatus = useCallback(async () => { @@ -18,6 +18,12 @@ export const ProtectedRoutes = () => { void checkUserStatus(); }, [checkUserStatus]); + useEffect(() => { + if (currentUser.isAuthenticated) { + return subscribeToUser(); + } + }, [currentUser.isAuthenticated, subscribeToUser]); + if (isLoading) { // It's better to make a fading effect to disappear the loading page return ; @@ -30,7 +36,7 @@ const StartLoading = () => { return (
- +
); @@ -44,6 +50,6 @@ const SplashScreen = ({ isAuthenticated }: { isAuthenticated: boolean }) => { ); } else { - return ; + return ; } }; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/Welcome.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/auth/Welcome.tsx new file mode 100644 index 0000000000..2dd0401412 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/auth/Welcome.tsx @@ -0,0 +1,57 @@ +import { ReactComponent as AppflowyLogo } from '$app/assets/logo.svg'; +import Button from '@mui/material/Button'; +import { useTranslation } from 'react-i18next'; +import { LoginButtonGroup } from '$app/components/_shared/login'; +import { useNavigate } from 'react-router-dom'; +import { useAuth } from '$app/components/auth/auth.hooks'; + +export const Welcome = () => { + const { signInAsAnonymous } = useAuth(); + const { t } = useTranslation(); + const navigate = useNavigate(); + + return ( + <> +
e.preventDefault()} method='POST'> +
+
+ +
+ +
+ + {t('welcomeTo')} {t('appName')} + +
+ +
+ +
+
+ {t('signIn.or')} +
+
+
+ +
+
+
+ + + ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts index b9342fb210..56e44a4765 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts @@ -1,29 +1,55 @@ import { currentUserActions } from '$app_reducers/current-user/slice'; -import { UserProfilePB } from '@/services/backend/events/flowy-user'; +import { AuthenticatorPB, UserNotification, UserProfilePB } from '@/services/backend/events/flowy-user'; import { UserService } from '$app/application/user/user.service'; import { AuthService } from '$app/application/user/auth.service'; import { useAppSelector, useAppDispatch } from '$app/stores/store'; import { getCurrentWorkspaceSetting } from '$app/application/folder/workspace.service'; import { useCallback } from 'react'; +import { subscribeNotifications } from '$app/application/notification'; +import { nanoid } from 'nanoid'; export const useAuth = () => { const dispatch = useAppDispatch(); const currentUser = useAppSelector((state) => state.currentUser); + // Subscribe to user update events + const subscribeToUser = useCallback(() => { + const unsubscribePromise = subscribeNotifications({ + [UserNotification.DidUpdateUserProfile]: async (changeset) => { + dispatch( + currentUserActions.updateUser({ + email: changeset.email, + displayName: changeset.name, + iconUrl: changeset.icon_url, + }) + ); + }, + }); + + return () => { + void unsubscribePromise.then((fn) => fn()); + }; + }, [dispatch]); + + // Check if the user is authenticated const checkUser = useCallback(async () => { const userProfile = await UserService.getUserProfile(); if (!userProfile) return; const workspaceSetting = await getCurrentWorkspaceSetting(); + const isLocal = userProfile.authenticator === AuthenticatorPB.Local; + dispatch( currentUserActions.checkUser({ id: userProfile.id, token: userProfile.token, email: userProfile.email, displayName: userProfile.name, + iconUrl: userProfile.icon_url, isAuthenticated: true, workspaceSetting: workspaceSetting, + isLocal, }) ); @@ -38,18 +64,16 @@ export const useAuth = () => { // contains the latest visiting page and the current workspace data. const workspaceSetting = await getCurrentWorkspaceSetting(); - if (workspaceSetting) { - dispatch( - currentUserActions.updateUser({ - id: userProfile.id, - token: userProfile.token, - email: userProfile.email, - displayName: userProfile.name, - isAuthenticated: true, - workspaceSetting: workspaceSetting, - }) - ); - } + dispatch( + currentUserActions.updateUser({ + id: userProfile.id, + token: userProfile.token, + email: userProfile.email, + displayName: userProfile.name, + isAuthenticated: true, + workspaceSetting, + }) + ); return userProfile; }, @@ -80,5 +104,13 @@ export const useAuth = () => { dispatch(currentUserActions.logout()); }, [dispatch]); - return { currentUser, checkUser, register, login, logout }; + const signInAsAnonymous = useCallback(async () => { + const fakeEmail = nanoid(8) + '@appflowy.io'; + const fakePassword = 'AppFlowy123@'; + const fakeName = 'Me'; + + await register(fakeEmail, fakePassword, fakeName); + }, [register]); + + return { currentUser, checkUser, register, login, logout, subscribeToUser, signInAsAnonymous }; }; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/get_started/GetStarted.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/auth/get_started/GetStarted.tsx deleted file mode 100644 index 6bb693768a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/get_started/GetStarted.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { AppflowyLogo } from '../../_shared/svg/AppflowyLogo'; -import Button from '@mui/material/Button'; -import { useLogin } from '$app/components/auth/get_started/useLogin'; -import { useTranslation } from 'react-i18next'; - -export const GetStarted = () => { - const { onAutoSignInClick } = useLogin(); - const { t } = useTranslation(); - - return ( - <> -
e.preventDefault()} method='POST'> -
-
- -
- -
- - {t('signIn.loginTitle').replace('@:appName', 'AppFlowy')} - -
- -
- -
-
-
- - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/get_started/useLogin.ts b/frontend/appflowy_tauri/src/appflowy_app/components/auth/get_started/useLogin.ts deleted file mode 100644 index 15d607e812..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/get_started/useLogin.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { useState } from 'react'; -import { currentUserActions } from '$app_reducers/current-user/slice'; -import { useAppDispatch } from '$app/stores/store'; -import { useNavigate } from 'react-router-dom'; -import { useAuth } from '../auth.hooks'; -import { nanoid } from 'nanoid'; - -export const useLogin = () => { - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [showPassword, setShowPassword] = useState(false); - const appDispatch = useAppDispatch(); - const navigate = useNavigate(); - const { login, register } = useAuth(); - const [authError, setAuthError] = useState(false); - - function onTogglePassword() { - setShowPassword(!showPassword); - } - - // reset error - function _setEmail(v: string) { - setAuthError(false); - setEmail(v); - } - - function _setPassword(v: string) { - setAuthError(false); - setPassword(v); - } - - async function onAutoSignInClick() { - try { - const fakeEmail = nanoid(8) + '@appflowy.io'; - const fakePassword = 'AppFlowy123@'; - const userProfile = await register(fakeEmail, fakePassword, 'Me'); - const { id, name, token } = userProfile; - - appDispatch( - currentUserActions.updateUser({ - id: id, - displayName: name, - email: email, - token: token, - isAuthenticated: true, - }) - ); - navigate('/'); - } catch (e) { - setAuthError(true); - } - } - - async function onSignInClick() { - try { - const userProfile = await login(email, password); - const { id, name, token } = userProfile; - - appDispatch( - currentUserActions.updateUser({ - id: id, - displayName: name, - email: email, - token: token, - isAuthenticated: true, - }) - ); - navigate('/'); - } catch (e) { - setAuthError(true); - } - } - - return { - showPassword, - onTogglePassword, - onSignInClick, - onAutoSignInClick, - email, - setEmail: _setEmail, - password, - setPassword: _setPassword, - authError, - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/TimeFormat.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/TimeFormat.tsx index e95ac20c8f..89a9ad1756 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/TimeFormat.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/TimeFormat.tsx @@ -22,7 +22,7 @@ function TimeFormat({ value, onChange }: Props) { return (
{title}
- {value === option && } + {value === option && }
); }, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFormatMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFormatMenu.tsx index 13571244b3..0f9be6a21a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFormatMenu.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFormatMenu.tsx @@ -29,7 +29,7 @@ function NumberFormatMenu({ return ( <> {formatText(format)} - {value === format && } + {value === format && } ); }, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/SelectOptionModifyMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/SelectOptionModifyMenu.tsx index 63b5872c2e..6c6cf37aae 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/SelectOptionModifyMenu.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/SelectOptionModifyMenu.tsx @@ -156,7 +156,7 @@ export const SelectOptionModifyMenu: FC = ({ fieldId, opt > {t(`grid.selectOption.${SelectOptionColorTextMap[color]}`)} - {option.color === color && } + {option.color === color && } ))} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectOptionItem.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectOptionItem.tsx index 27461932b3..2a855a4085 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectOptionItem.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectOptionItem.tsx @@ -32,7 +32,7 @@ export const SelectOptionItem: FC = ({ isSelected, fieldI
- {isSelected && !hovered && } + {isSelected && !hovered && } {hovered && ( diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/select_filter/SelectFilter.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/select_filter/SelectFilter.tsx index 3bf0453256..093c1e22b9 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/select_filter/SelectFilter.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/select_filter/SelectFilter.tsx @@ -32,7 +32,7 @@ function SelectFilter({ onClose, filter, field, onChange }: Props) { content: (
- {filter.data.optionIds?.includes(option.id) && } + {filter.data.optionIds?.includes(option.id) && }
), }; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenu.tsx index 890a4e2a33..e3021249ee 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenu.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenu.tsx @@ -39,7 +39,7 @@ export const PropertyTypeMenu: FC< - {type === field.type && } + {type === field.type && } ); }, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/FormulaEditPopover.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/FormulaEditPopover.tsx index 8f85a24d61..c60d7af40e 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/FormulaEditPopover.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/FormulaEditPopover.tsx @@ -71,7 +71,7 @@ function FormulaEditPopover({ /> onDone(text)}> - + diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/error/ErrorModal.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/error/ErrorModal.tsx index 30b9822fa4..6da2ee96d0 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/error/ErrorModal.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/error/ErrorModal.tsx @@ -1,24 +1,22 @@ -import { InformationSvg } from '../_shared/svg/InformationSvg'; -import { CloseSvg } from '../_shared/svg/CloseSvg'; +import { ReactComponent as InformationSvg } from '$app/assets/information.svg'; +import { ReactComponent as CloseSvg } from '$app/assets/close.svg'; export const ErrorModal = ({ message, onClose }: { message: string; onClose: () => void }) => { return (
-
- +
+

Oops.. something went wrong

{message}

diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Layout.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/Layout.tsx index 14bc179189..4647eb59a6 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Layout.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/Layout.tsx @@ -12,10 +12,6 @@ function Layout({ children }: { children: ReactNode }) { if (e.key === 'Backspace' && e.target instanceof HTMLBodyElement) { e.preventDefault(); } - - if (e.key === 'Escape') { - e.preventDefault(); - } }; window.addEventListener('keydown', onKeyDown); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/layout.scss b/frontend/appflowy_tauri/src/appflowy_app/components/layout/layout.scss index 5183e4010f..3949673c89 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/layout.scss +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/layout.scss @@ -55,4 +55,15 @@ &:hover { background-color: rgba(156, 156, 156, 0.20); } +} + +.theme-mode-item { + @apply relative flex h-[72px] w-[88px] cursor-pointer items-end justify-end rounded border hover:shadow; + background: linear-gradient(150.74deg, rgba(231, 231, 231, 0) 17.95%, #C5C5C5 95.51%); +} + +[data-dark-mode="true"] { + .theme-mode-item { + background: linear-gradient(150.74deg, rgba(128, 125, 125, 0) 17.95%, #4d4d4d 95.51%); + } } \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/SideBar.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/SideBar.tsx index 02e8bfb60b..5cdbfb125b 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/SideBar.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/SideBar.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useRef } from 'react'; import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { AppflowyLogoDark } from '$app/components/_shared/svg/AppflowyLogoDark'; -import { AppflowyLogoLight } from '$app/components/_shared/svg/AppflowyLogoLight'; +import { ReactComponent as AppflowyLogoDark } from '$app/assets/dark-logo.svg'; +import { ReactComponent as AppflowyLogoLight } from '$app/assets/light-logo.svg'; import CollapseMenuButton from '$app/components/layout/collapse_menu_button/CollapseMenuButton'; import Resizer from '$app/components/layout/side_bar/Resizer'; import UserInfo from '$app/components/layout/side_bar/UserInfo'; @@ -50,7 +50,11 @@ function SideBar() { >
- {isDark ? : } + {isDark ? ( + + ) : ( + + )}
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/UserInfo.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/UserInfo.tsx index b02620e88c..62763c670e 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/UserInfo.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/UserInfo.tsx @@ -1,11 +1,11 @@ import React, { useState } from 'react'; import { useAppSelector } from '$app/stores/store'; -import { Avatar, IconButton } from '@mui/material'; -import PersonOutline from '@mui/icons-material/PersonOutline'; -import UserSetting from '../user_setting/UserSetting'; +import { IconButton } from '@mui/material'; import { ReactComponent as SettingIcon } from '$app/assets/settings.svg'; import Tooltip from '@mui/material/Tooltip'; import { useTranslation } from 'react-i18next'; +import { SettingsDialog } from '$app/components/settings/SettingsDialog'; +import { ProfileAvatar } from '$app/components/_shared/avatar'; function UserInfo() { const currentUser = useAppSelector((state) => state.currentUser); @@ -16,19 +16,9 @@ function UserInfo() { return ( <>
-
- - {currentUser.displayName ? currentUser.displayName[0] : } - - {currentUser.displayName} +
+ + {currentUser.displayName}
@@ -43,7 +33,7 @@ function UserInfo() {
- setShowUserSetting(false)} /> + {showUserSetting && setShowUserSetting(false)} />} ); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreButton.tsx index 1ffae1eb85..d37d1bf060 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreButton.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreButton.tsx @@ -1,7 +1,7 @@ import React, { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { Drawer, IconButton } from '@mui/material'; -import { Details2Svg } from '$app/components/_shared/svg/Details2Svg'; +import { ReactComponent as Details2Svg } from '$app/assets/details.svg'; import Tooltip from '@mui/material/Tooltip'; import MoreOptions from '$app/components/layout/top_bar/MoreOptions'; import { useMoreOptionsConfig } from '$app/components/layout/top_bar/MoreOptions.hooks'; @@ -18,8 +18,8 @@ function MoreButton() { return ( <> - toggleDrawer(true)} className={'h-8 w-8 text-icon-primary'}> - + toggleDrawer(true)} className={'text-icon-primary'}> + toggleDrawer(false)}> diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/AppearanceSetting.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/AppearanceSetting.tsx deleted file mode 100644 index e28a467526..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/AppearanceSetting.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import Select from '@mui/material/Select'; -import { Theme, ThemeMode, UserSetting } from '$app/stores/reducers/current-user/slice'; -import MenuItem from '@mui/material/MenuItem'; -import { useTranslation } from 'react-i18next'; - -function AppearanceSetting({ - themeMode = ThemeMode.System, - onChange, -}: { - theme?: Theme; - themeMode?: ThemeMode; - onChange: (setting: UserSetting) => void; -}) { - const { t } = useTranslation(); - - const themeModeOptions = useMemo( - () => [ - { - value: ThemeMode.Light, - content: t('settings.appearance.themeMode.light'), - }, - { - value: ThemeMode.Dark, - content: t('settings.appearance.themeMode.dark'), - }, - { - value: ThemeMode.System, - content: t('settings.appearance.themeMode.system'), - }, - ], - [t] - ); - - const renderSelect = useCallback( - ( - items: { - options: { value: ThemeMode | Theme; content: string }[]; - label: string; - value: ThemeMode | Theme; - onChange: (newValue: ThemeMode | Theme) => void; - }[] - ) => { - return items.map((item) => { - const { value, options, label, onChange } = item; - - return ( -
-
{label}
-
- -
-
- ); - }); - }, - [] - ); - - return ( -
- {renderSelect([ - { - options: themeModeOptions, - label: t('settings.appearance.themeMode.label'), - value: themeMode, - onChange: (newValue) => { - onChange({ - themeMode: newValue as ThemeMode, - isDark: - newValue === ThemeMode.Dark || - (newValue === ThemeMode.System && window.matchMedia('(prefers-color-scheme: dark)').matches), - }); - }, - }, - ])} -
- ); -} - -export default AppearanceSetting; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/LanguageSetting.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/LanguageSetting.tsx deleted file mode 100644 index 81e7d067a4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/LanguageSetting.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import Select from '@mui/material/Select'; -import { UserSetting } from '$app/stores/reducers/current-user/slice'; -import MenuItem from '@mui/material/MenuItem'; - -const languages = [ - { - key: 'ar-SA', - title: 'العربية', - }, - { key: 'ca-ES', title: 'Català' }, - { key: 'de-DE', title: 'Deutsch' }, - { key: 'en', title: 'English' }, - { key: 'es-VE', title: 'Español (Venezuela)' }, - { key: 'eu-ES', title: 'Español' }, - { key: 'fr-FR', title: 'Français' }, - { key: 'hu-HU', title: 'Magyar' }, - { key: 'id-ID', title: 'Bahasa Indonesia' }, - { key: 'it-IT', title: 'Italiano' }, - { key: 'ja-JP', title: '日本語' }, - { key: 'ko-KR', title: '한국어' }, - { key: 'pl-PL', title: 'Polski' }, - { key: 'pt-BR', title: 'Português' }, - { key: 'pt-PT', title: 'Português' }, - { key: 'ru-RU', title: 'Русский' }, - { key: 'sv', title: 'Svenska' }, - { key: 'th-TH', title: 'ไทย' }, - { key: 'tr-TR', title: 'Türkçe' }, - { key: 'zh-CN', title: '简体中文' }, - { key: 'zh-TW', title: '繁體中文' }, -]; - -function LanguageSetting({ - language = 'en', - onChange, -}: { - language?: string; - onChange: (setting: UserSetting) => void; -}) { - const { t, i18n } = useTranslation(); - - return ( -
-
-
{t('settings.menu.language')}
-
- -
-
-
- ); -} - -export default LanguageSetting; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/Menu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/Menu.tsx deleted file mode 100644 index 9da3cb8f74..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/Menu.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import React, { useMemo } from 'react'; -import LanguageIcon from '@mui/icons-material/Language'; -import PaletteOutlined from '@mui/icons-material/PaletteOutlined'; -import { useTranslation } from 'react-i18next'; - -export enum MenuItem { - Appearance = 'Appearance', - Language = 'Language', -} - -function UserSettingMenu({ selected, onSelect }: { onSelect: (selected: MenuItem) => void; selected: MenuItem }) { - const { t } = useTranslation(); - - const options = useMemo(() => { - return [ - { - label: t('settings.menu.appearance'), - value: MenuItem.Appearance, - icon: , - }, - { - label: t('settings.menu.language'), - value: MenuItem.Language, - icon: , - }, - ]; - }, [t]); - - return ( -
- {options.map((option) => { - return ( -
{ - onSelect(option.value); - }} - className={`my-1 flex w-full cursor-pointer items-center justify-start rounded-md p-2 text-xs text-text-title ${ - selected === option.value ? 'bg-fill-list-hover' : 'hover:text-content-blue-300' - }`} - > -
{option.icon}
-
{option.label}
-
- ); - })} -
- ); -} - -export default UserSettingMenu; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/SettingPanel.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/SettingPanel.tsx deleted file mode 100644 index c88fe1f2e3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/SettingPanel.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React, { useMemo } from 'react'; -import { MenuItem } from './Menu'; -import AppearanceSetting from './AppearanceSetting'; -import LanguageSetting from './LanguageSetting'; - -import { UserSetting } from '$app/stores/reducers/current-user/slice'; - -function UserSettingPanel({ - selected, - userSettingState = {}, - onChange, -}: { - selected: MenuItem; - userSettingState?: UserSetting; - onChange: (setting: Partial) => void; -}) { - const { theme, themeMode, language } = userSettingState; - - const options = useMemo(() => { - return [ - { - value: MenuItem.Appearance, - content: , - }, - { - value: MenuItem.Language, - content: , - }, - ]; - }, [language, onChange, theme, themeMode]); - - const option = options.find((option) => option.value === selected); - - return
{option?.content}
; -} - -export default UserSettingPanel; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/UserSetting.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/UserSetting.tsx deleted file mode 100644 index 7cfbac4a76..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/UserSetting.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React, { useCallback, useState } from 'react'; -import Dialog from '@mui/material/Dialog'; -import DialogContent from '@mui/material/DialogContent'; -import DialogTitle from '@mui/material/DialogTitle'; -import Slide, { SlideProps } from '@mui/material/Slide'; -import UserSettingMenu, { MenuItem } from './Menu'; -import UserSettingPanel from './SettingPanel'; -import { UserSetting } from '$app/stores/reducers/current-user/slice'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { currentUserActions } from '$app_reducers/current-user/slice'; -import { useTranslation } from 'react-i18next'; -import { UserService } from '$app/application/user/user.service'; - -const SlideTransition = React.forwardRef((props: SlideProps, ref) => { - return ; -}); - -function UserSettings({ open, onClose }: { open: boolean; onClose: () => void }) { - const userSettingState = useAppSelector((state) => state.currentUser.userSetting); - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - const [selected, setSelected] = useState(MenuItem.Appearance); - const handleChange = useCallback( - (setting: Partial) => { - const newSetting = { ...userSettingState, ...setting }; - - dispatch(currentUserActions.setUserSetting(newSetting)); - const language = newSetting.language || 'en'; - - void UserService.setAppearanceSetting({ - theme: newSetting.theme, - theme_mode: newSetting.themeMode, - locale: { - language_code: language.split('-')[0], - country_code: language.split('-')[1], - }, - }); - }, - [dispatch, userSettingState] - ); - - return ( - e.stopPropagation()} - open={open} - TransitionComponent={SlideTransition} - keepMounted={false} - onClose={onClose} - > - {t('settings.title')} - - { - setSelected(selected); - }} - selected={selected} - /> - - - - ); -} - -export default UserSettings; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/TrashButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/TrashButton.tsx index e89af36869..984ed6f67f 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/TrashButton.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/TrashButton.tsx @@ -1,5 +1,5 @@ import React, { useCallback } from 'react'; -import { TrashSvg } from '$app/components/_shared/svg/TrashSvg'; +import { ReactComponent as TrashSvg } from '$app/assets/delete.svg'; import { useTranslation } from 'react-i18next'; import { useLocation, useNavigate } from 'react-router-dom'; import { useDrag } from 'src/appflowy_app/components/_shared/drag_block'; @@ -34,9 +34,7 @@ function TrashButton() { selected ? 'bg-fill-list-active' : '' } ${isDraggingOver ? 'bg-fill-list-hover' : ''}`} > -
- -
+ {t('trash.text')}
); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.hooks.ts index 7d77b12d69..a754dcdf3b 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.hooks.ts @@ -80,6 +80,13 @@ export function useLoadWorkspace(workspace: WorkspaceItem) { useEffect(() => { const unsubscribePromise = subscribeNotifications( { + [FolderNotification.DidUpdateWorkspace]: async (changeset) => { + dispatch( + workspaceActions.updateCurrentWorkspace({ + name: changeset.name, + }) + ); + }, [FolderNotification.DidUpdateWorkspaceViews]: async (changeset) => { const res = changeset.items; @@ -90,7 +97,7 @@ export function useLoadWorkspace(workspace: WorkspaceItem) { ); return () => void unsubscribePromise.then((unsubscribe) => unsubscribe()); - }, [id, onChildPagesChanged]); + }, [dispatch, id, onChildPagesChanged]); return { openWorkspace, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.tsx index 0035ba702f..b2f0fbb0ca 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.tsx @@ -2,11 +2,11 @@ import React, { useState } from 'react'; import { WorkspaceItem } from '$app_reducers/workspace/slice'; import NestedViews from '$app/components/layout/workspace_manager/NestedPages'; import { useLoadWorkspace, useWorkspaceActions } from '$app/components/layout/workspace_manager/Workspace.hooks'; -import Typography from '@mui/material/Typography'; import { useTranslation } from 'react-i18next'; import { ReactComponent as AddIcon } from '$app/assets/add.svg'; import { IconButton } from '@mui/material'; import Tooltip from '@mui/material/Tooltip'; +import { WorkplaceAvatar } from '$app/components/_shared/avatar'; function Workspace({ workspace, opened }: { workspace: WorkspaceItem; opened: boolean }) { useLoadWorkspace(workspace); @@ -42,9 +42,10 @@ function Workspace({ workspace, opened }: { workspace: WorkspaceItem; opened: bo className={'mt-2 flex h-[22px] w-full cursor-pointer select-none items-center justify-between px-4'} > - - {t('sideBar.personal')} - +
+ + {workspace.name} +
{showAdd && ( diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/Login.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/Login.tsx new file mode 100644 index 0000000000..6daceadb61 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/settings/Login.tsx @@ -0,0 +1,22 @@ +import Typography from '@mui/material/Typography'; +import { useTranslation } from 'react-i18next'; +import Button from '@mui/material/Button'; +import { LoginButtonGroup } from '$app/components/_shared/login'; + +export const Login = ({ onBack }: { onBack?: () => void }) => { + const { t } = useTranslation(); + + return ( +
+ + {t('button.login')} + +
+ + +
+
+ ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/Settings.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/Settings.tsx new file mode 100644 index 0000000000..1d9f3c0cd9 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/settings/Settings.tsx @@ -0,0 +1,92 @@ +import { useMemo, useState } from 'react'; +import { Box, Tab, Tabs } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { MyAccount } from '$app/components/settings/my_account'; +import { ReactComponent as AccountIcon } from '$app/assets/settings/account.svg'; +import { ReactComponent as WorkplaceIcon } from '$app/assets/settings/workplace.svg'; +import { Workplace } from '$app/components/settings/workplace'; +import { SettingsRoutes } from '$app/components/settings/workplace/const'; + +interface TabPanelProps { + children?: React.ReactNode; + index: number; + value: number; +} + +function TabPanel(props: TabPanelProps) { + const { children, value, index, ...other } = props; + + return ( + + ); +} + +export const Settings = ({ onForward }: { onForward: (route: SettingsRoutes) => void }) => { + const { t } = useTranslation(); + const [activeTab, setActiveTab] = useState(0); + + const tabOptions = useMemo(() => { + return [ + { + label: t('newSettings.myAccount.title'), + Icon: AccountIcon, + Component: MyAccount, + }, + { + label: t('newSettings.workplace.name'), + Icon: WorkplaceIcon, + Component: Workplace, + }, + ]; + }, [t]); + + const handleChangeTab = (event: React.SyntheticEvent, newValue: number) => { + setActiveTab(newValue); + }; + + return ( + + + {tabOptions.map((tab, index) => ( + + + {tab.label} +
+ } + onClick={() => setActiveTab(index)} + sx={{ '&.Mui-selected': { borderColor: 'transparent', backgroundColor: 'var(--fill-list-active)' } }} + /> + ))} + + {tabOptions.map((tab, index) => ( + + + + ))} + + ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/SettingsDialog.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/SettingsDialog.tsx new file mode 100644 index 0000000000..72f2fddb49 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/settings/SettingsDialog.tsx @@ -0,0 +1,93 @@ +/** + * @figmaUrl https://www.figma.com/file/MF5CWlOzBRuGHp45zAXyUH/Appflowy%3A-Desktop-Settings?type=design&node-id=100%3A2119&mode=design&t=4Wb0Zg5NOFO36kOf-1 + */ + +import Dialog, { DialogProps } from '@mui/material/Dialog'; +import { Settings } from '$app/components/settings/Settings'; +import { useCallback, useState } from 'react'; +import DialogTitle from '@mui/material/DialogTitle'; +import { IconButton } from '@mui/material'; +import { ReactComponent as CloseIcon } from '$app/assets/close.svg'; +import { useTranslation } from 'react-i18next'; +import { ReactComponent as UpIcon } from '$app/assets/up.svg'; +import { SettingsRoutes } from '$app/components/settings/workplace/const'; +import DialogContent from '@mui/material/DialogContent'; +import { Login } from '$app/components/settings/Login'; +import SwipeableViews from 'react-swipeable-views'; + +export const SettingsDialog = (props: DialogProps) => { + const [routes, setRoutes] = useState([]); + + const { t } = useTranslation(); + const handleForward = useCallback((route: SettingsRoutes) => { + setRoutes((prev) => { + return [...prev, route]; + }); + }, []); + + const handleBack = useCallback(() => { + setRoutes((prevState) => { + return prevState.slice(0, -1); + }); + }, []); + + const handleClose = () => { + props?.onClose?.({}, 'backdropClick'); + }; + + const currentRoute = routes[routes.length - 1]; + + return ( + { + if (e.key === 'Escape') { + e.preventDefault(); + } + }} + scroll={'paper'} + > + + + + + + ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/settings/index.ts new file mode 100644 index 0000000000..0f0a2c23f4 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/settings/index.ts @@ -0,0 +1 @@ +export * from './my_account'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/AccountLogin.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/AccountLogin.tsx new file mode 100644 index 0000000000..0178682ca8 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/AccountLogin.tsx @@ -0,0 +1,30 @@ +import { useTranslation } from 'react-i18next'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; +import { Divider } from '@mui/material'; +import { DeleteAccount } from '$app/components/settings/my_account/DeleteAccount'; +import { SettingsRoutes } from '$app/components/settings/workplace/const'; + +export const AccountLogin = ({ onForward }: { onForward?: (route: SettingsRoutes) => void }) => { + const { t } = useTranslation(); + + return ( + <> +
+ + {t('newSettings.myAccount.accountLogin')} + + + + +
+ + ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/DeleteAccount.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/DeleteAccount.tsx new file mode 100644 index 0000000000..82a909180e --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/DeleteAccount.tsx @@ -0,0 +1,43 @@ +import { useTranslation } from 'react-i18next'; +import { useState } from 'react'; +import Typography from '@mui/material/Typography'; +import Button from '@mui/material/Button'; +import { DeleteAccountDialog } from '$app/components/settings/my_account/DeleteAccountDialog'; + +export const DeleteAccount = () => { + const { t } = useTranslation(); + + const [openDialog, setOpenDialog] = useState(false); + + return ( +
+
+ + {t('newSettings.myAccount.deleteAccount.title')} + + + {t('newSettings.myAccount.deleteAccount.subtitle')} + +
+
+ +
+ { + setOpenDialog(false); + }} + /> +
+ ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/DeleteAccountDialog.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/DeleteAccountDialog.tsx new file mode 100644 index 0000000000..2f8cc37258 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/DeleteAccountDialog.tsx @@ -0,0 +1,50 @@ +import Dialog, { DialogProps } from '@mui/material/Dialog'; +import { useTranslation } from 'react-i18next'; +import DialogTitle from '@mui/material/DialogTitle'; +import { DialogActions, DialogContentText, IconButton } from '@mui/material'; +import Button from '@mui/material/Button'; +import DialogContent from '@mui/material/DialogContent'; +import { ReactComponent as CloseIcon } from '$app/assets/close.svg'; + +export const DeleteAccountDialog = (props: DialogProps) => { + const { t } = useTranslation(); + + const handleClose = () => { + props?.onClose?.({}, 'backdropClick'); + }; + + const handleOk = () => { + //123 + }; + + return ( + + {t('newSettings.myAccount.deleteAccount.dialogTitle')} + + {t('newSettings.myAccount.deleteAccount.dialogContent1')} + {t('newSettings.myAccount.deleteAccount.dialogContent2')} + + +
+ +
+
+ +
+
+ + + +
+ ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/MyAccount.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/MyAccount.tsx new file mode 100644 index 0000000000..b3a315994b --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/MyAccount.tsx @@ -0,0 +1,24 @@ +import { useTranslation } from 'react-i18next'; +import Typography from '@mui/material/Typography'; +import { Profile } from './Profile'; +import { AccountLogin } from './AccountLogin'; +import { Divider } from '@mui/material'; +import { SettingsRoutes } from '$app/components/settings/workplace/const'; + +export const MyAccount = ({ onForward }: { onForward?: (route: SettingsRoutes) => void }) => { + const { t } = useTranslation(); + + return ( +
+ + {t('newSettings.myAccount.title')} + + + {t('newSettings.myAccount.subtitle')} + + + + +
+ ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/Profile.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/Profile.tsx new file mode 100644 index 0000000000..2ac672b0e5 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/Profile.tsx @@ -0,0 +1,180 @@ +import Typography from '@mui/material/Typography'; +import { useTranslation } from 'react-i18next'; +import { IconButton, InputAdornment, OutlinedInput } from '@mui/material'; +import { useAppSelector } from '$app/stores/store'; +import React, { useState } from 'react'; +import { ReactComponent as CheckIcon } from '$app/assets/select-check.svg'; +import { ReactComponent as CloseIcon } from '$app/assets/close.svg'; +import { ReactComponent as EditIcon } from '$app/assets/edit.svg'; + +import Tooltip from '@mui/material/Tooltip'; +import { UserService } from '$app/application/user/user.service'; +import { notify } from '$app/components/_shared/notify'; +import { ProfileAvatar } from '$app/components/_shared/avatar'; +import Popover from '@mui/material/Popover'; +import { PopoverCommonProps } from '$app/components/editor/components/tools/popover'; +import EmojiPicker from '$app/components/_shared/emoji_picker/EmojiPicker'; +import Button from '@mui/material/Button'; + +export const Profile = () => { + const { displayName, id } = useAppSelector((state) => state.currentUser); + const { t } = useTranslation(); + const [isEditing, setIsEditing] = useState(false); + const [newName, setNewName] = useState(displayName ?? 'Me'); + const [error, setError] = useState(false); + const [emojiPickerAnchor, setEmojiPickerAnchor] = useState(null); + const openEmojiPicker = Boolean(emojiPickerAnchor); + const handleSave = async () => { + setError(false); + if (!newName) { + setError(true); + return; + } + + if (newName === displayName) { + setIsEditing(false); + return; + } + + try { + await UserService.updateUserProfile({ + id, + name: newName, + }); + setIsEditing(false); + } catch { + setError(true); + notify.error(t('newSettings.myAccount.updateNameError')); + } + }; + + const handleEmojiSelect = async (emoji: string) => { + try { + await UserService.updateUserProfile({ + id, + icon_url: emoji, + }); + } catch { + notify.error(t('newSettings.myAccount.updateIconError')); + } + }; + + const handleCancel = () => { + setNewName(displayName ?? 'Me'); + setIsEditing(false); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + e.stopPropagation(); + e.preventDefault(); + void handleSave(); + } + + if (e.key === 'Escape') { + e.stopPropagation(); + e.preventDefault(); + handleCancel(); + } + }; + + return ( +
+ + {t('newSettings.myAccount.profileLabel')} + +
+ + +
+ {isEditing ? ( + setNewName(e.target.value)} + spellCheck={false} + autoFocus={true} + autoCorrect={'off'} + autoCapitalize={'off'} + fullWidth + endAdornment={ + +
+ + + + + + + + + + +
+
+ } + sx={{ + '&.MuiOutlinedInput-root': { + borderRadius: '8px', + }, + }} + placeholder={t('newSettings.myAccount.profileNamePlaceholder')} + value={newName} + /> + ) : ( + + {newName} + + setIsEditing(true)} size={'small'} className={'ml-1'}> + + + + + )} +
+
+ {openEmojiPicker && ( + { + setEmojiPickerAnchor(null); + }} + anchorOrigin={{ + vertical: 'bottom', + horizontal: 'left', + }} + transformOrigin={{ + vertical: 'top', + horizontal: 'left', + }} + > + { + setEmojiPickerAnchor(null); + }} + onEmojiSelect={handleEmojiSelect} + /> + + )} +
+ ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/index.ts new file mode 100644 index 0000000000..d923fcefce --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/index.ts @@ -0,0 +1 @@ +export * from './MyAccount'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/Appearance.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/Appearance.tsx new file mode 100644 index 0000000000..1dc8581dae --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/Appearance.tsx @@ -0,0 +1,20 @@ +import { useTranslation } from 'react-i18next'; +import { ThemeModeSwitch } from '$app/components/settings/workplace/appearance/ThemeModeSwitch'; +import Typography from '@mui/material/Typography'; +import { Divider } from '@mui/material'; +import { LanguageSetting } from '$app/components/settings/workplace/appearance/LanguageSetting'; + +export const Appearance = () => { + const { t } = useTranslation(); + + return ( + <> + + {t('newSettings.workplace.appearance.name')} + + + + + + ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/Workplace.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/Workplace.tsx new file mode 100644 index 0000000000..8af69eec51 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/Workplace.tsx @@ -0,0 +1,23 @@ +import { useTranslation } from 'react-i18next'; +import Typography from '@mui/material/Typography'; +import { WorkplaceDisplay } from '$app/components/settings/workplace/WorkplaceDisplay'; +import { Divider } from '@mui/material'; +import { Appearance } from '$app/components/settings/workplace/Appearance'; + +export const Workplace = () => { + const { t } = useTranslation(); + + return ( +
+ + {t('newSettings.workplace.title')} + + + {t('newSettings.workplace.subtitle')} + + + + +
+ ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/WorkplaceDisplay.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/WorkplaceDisplay.tsx new file mode 100644 index 0000000000..cbb68e5d56 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/WorkplaceDisplay.tsx @@ -0,0 +1,128 @@ +import { useTranslation } from 'react-i18next'; +import Typography from '@mui/material/Typography'; +import { Divider, OutlinedInput } from '@mui/material'; +import React, { useState } from 'react'; +import Button from '@mui/material/Button'; +import { useAppSelector } from '$app/stores/store'; +import { changeWorkspaceIcon, renameWorkspace } from '$app/application/folder/workspace.service'; +import { notify } from '$app/components/_shared/notify'; +import { WorkplaceAvatar } from '$app/components/_shared/avatar'; +import Popover from '@mui/material/Popover'; +import { PopoverCommonProps } from '$app/components/editor/components/tools/popover'; +import EmojiPicker from '$app/components/_shared/emoji_picker/EmojiPicker'; + +export const WorkplaceDisplay = () => { + const { t } = useTranslation(); + const isLocal = useAppSelector((state) => state.currentUser.isLocal); + const workspace = useAppSelector((state) => state.workspace.currentWorkspace); + const [name, setName] = useState(workspace?.name ?? ''); + const [emojiPickerAnchor, setEmojiPickerAnchor] = useState(null); + const openEmojiPicker = Boolean(emojiPickerAnchor); + const handleSave = async () => { + if (!workspace || !name) return; + try { + await renameWorkspace(workspace.id, name); + } catch { + notify.error(t('newSettings.workplace.renameError')); + } + }; + + const handleEmojiSelect = async (icon: string) => { + if (!workspace) return; + try { + await changeWorkspaceIcon(workspace.id, icon); + } catch { + notify.error(t('newSettings.workplace.updateIconError')); + } + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + e.stopPropagation(); + e.preventDefault(); + void handleSave(); + } + }; + + return ( +
+ + {t('newSettings.workplace.workplaceName')} + +
+
+ setName(e.target.value)} + sx={{ + '&.MuiOutlinedInput-root': { + borderRadius: '8px', + }, + }} + placeholder={t('newSettings.workplace.workplaceNamePlaceholder')} + value={name} + /> +
+ +
+ + + {t('newSettings.workplace.workplaceIcon')} + + + {t('newSettings.workplace.workplaceIconSubtitle')} + + + {openEmojiPicker && ( + { + setEmojiPickerAnchor(null); + }} + anchorOrigin={{ + vertical: 'top', + horizontal: 'right', + }} + transformOrigin={{ + vertical: 'top', + horizontal: 'left', + }} + > + { + setEmojiPickerAnchor(null); + }} + onEmojiSelect={handleEmojiSelect} + /> + + )} +
+ ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/appearance/LanguageSetting.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/appearance/LanguageSetting.tsx new file mode 100644 index 0000000000..41a42bd011 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/appearance/LanguageSetting.tsx @@ -0,0 +1,115 @@ +import Typography from '@mui/material/Typography'; +import { useTranslation } from 'react-i18next'; +import MenuItem from '@mui/material/MenuItem'; +import Select from '@mui/material/Select'; +import React, { useCallback } from 'react'; +import { useAppDispatch, useAppSelector } from '$app/stores/store'; +import { currentUserActions } from '$app_reducers/current-user/slice'; +import { UserService } from '$app/application/user/user.service'; + +const languages = [ + { + key: 'ar-SA', + title: 'العربية', + }, + { key: 'ca-ES', title: 'Català' }, + { key: 'de-DE', title: 'Deutsch' }, + { key: 'en', title: 'English' }, + { key: 'es-VE', title: 'Español (Venezuela)' }, + { key: 'eu-ES', title: 'Español' }, + { key: 'fr-FR', title: 'Français' }, + { key: 'hu-HU', title: 'Magyar' }, + { key: 'id-ID', title: 'Bahasa Indonesia' }, + { key: 'it-IT', title: 'Italiano' }, + { key: 'ja-JP', title: '日本語' }, + { key: 'ko-KR', title: '한국어' }, + { key: 'pl-PL', title: 'Polski' }, + { key: 'pt-BR', title: 'Português' }, + { key: 'pt-PT', title: 'Português' }, + { key: 'ru-RU', title: 'Русский' }, + { key: 'sv', title: 'Svenska' }, + { key: 'th-TH', title: 'ไทย' }, + { key: 'tr-TR', title: 'Türkçe' }, + { key: 'zh-CN', title: '简体中文' }, + { key: 'zh-TW', title: '繁體中文' }, +]; + +export const LanguageSetting = () => { + const { t, i18n } = useTranslation(); + const userSettingState = useAppSelector((state) => state.currentUser.userSetting); + const dispatch = useAppDispatch(); + const selectedLanguage = userSettingState.language; + + const [hoverKey, setHoverKey] = React.useState(null); + + const handleChange = useCallback( + (language: string) => { + const newSetting = { ...userSettingState, language }; + + dispatch(currentUserActions.setUserSetting(newSetting)); + const newLanguage = newSetting.language || 'en'; + + void UserService.setAppearanceSetting({ + theme: newSetting.theme, + theme_mode: newSetting.themeMode, + locale: { + language_code: newLanguage.split('-')[0], + country_code: newLanguage.split('-')[1], + }, + }); + }, + [dispatch, userSettingState] + ); + + const handleKeyDown = useCallback((e: React.KeyboardEvent) => { + if (e.key === 'Escape') { + e.preventDefault(); + } + }, []); + + return ( + <> + + {t('newSettings.workplace.appearance.language')} + + + + + ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/appearance/ThemeModeSwitch.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/appearance/ThemeModeSwitch.tsx new file mode 100644 index 0000000000..34fdb8e598 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/appearance/ThemeModeSwitch.tsx @@ -0,0 +1,93 @@ +import { useTranslation } from 'react-i18next'; +import { useAppDispatch, useAppSelector } from '$app/stores/store'; +import { useCallback, useMemo } from 'react'; +import { ThemeModePB } from '@/services/backend'; +import darkSrc from '$app/assets/settings/dark.png'; +import lightSrc from '$app/assets/settings/light.png'; +import { currentUserActions, ThemeMode } from '$app_reducers/current-user/slice'; +import { UserService } from '$app/application/user/user.service'; +import { ReactComponent as CheckCircle } from '$app/assets/settings/check_circle.svg'; + +export const ThemeModeSwitch = () => { + const { t } = useTranslation(); + const userSettingState = useAppSelector((state) => state.currentUser.userSetting); + const dispatch = useAppDispatch(); + + const selectedMode = userSettingState.themeMode; + const themeModes = useMemo(() => { + return [ + { + name: t('newSettings.workplace.appearance.themeMode.auto'), + value: ThemeModePB.System, + img: ( +
+ + +
+ ), + }, + { + name: t('newSettings.workplace.appearance.themeMode.light'), + value: ThemeModePB.Light, + img: , + }, + { + name: t('newSettings.workplace.appearance.themeMode.dark'), + value: ThemeModePB.Dark, + img: , + }, + ]; + }, [t]); + + const handleChange = useCallback( + (newValue: ThemeModePB) => { + const newSetting = { + ...userSettingState, + ...{ + themeMode: newValue, + isDark: + newValue === ThemeMode.Dark || + (newValue === ThemeMode.System && window.matchMedia('(prefers-color-scheme: dark)').matches), + }, + }; + + dispatch(currentUserActions.setUserSetting(newSetting)); + + void UserService.setAppearanceSetting({ + theme_mode: newSetting.themeMode, + }); + }, + [dispatch, userSettingState] + ); + + const renderThemeModeItem = useCallback( + (option: { name: string; value: ThemeModePB; img: JSX.Element }) => { + return ( +
handleChange(option.value)} + className={'flex cursor-pointer flex-col items-center gap-2'} + > +
+ {option.img} + +
+
{option.name}
+
+ ); + }, + [handleChange, selectedMode] + ); + + return
{themeModes.map((mode) => renderThemeModeItem(mode))}
; +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/const.ts b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/const.ts new file mode 100644 index 0000000000..075e2744a5 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/const.ts @@ -0,0 +1,3 @@ +export enum SettingsRoutes { + LOGIN = 'login', +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/index.ts new file mode 100644 index 0000000000..a64592ac8b --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/index.ts @@ -0,0 +1 @@ +export * from './Workplace'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts index 13ad581175..193d77282f 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts @@ -22,14 +22,17 @@ export interface ICurrentUser { displayName?: string; email?: string; token?: string; + iconUrl?: string; isAuthenticated: boolean; workspaceSetting?: WorkspaceSettingPB; userSetting: UserSetting; + isLocal: boolean; } const initialState: ICurrentUser | null = { isAuthenticated: false, userSetting: {}, + isLocal: true, }; export const currentUserSlice = createSlice({ @@ -42,6 +45,7 @@ export const currentUserSlice = createSlice({ ...action.payload, }; }, + updateUser: (state, action: PayloadAction>) => { return { ...state, diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/workspace/slice.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/workspace/slice.ts index 090382de70..cbda3bb9ae 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/workspace/slice.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/workspace/slice.ts @@ -28,6 +28,14 @@ export const workspaceSlice = createSlice({ ) => { return action.payload; }, + + updateCurrentWorkspace: (state, action: PayloadAction>) => { + if (!state.currentWorkspace) return; + state.currentWorkspace = { + ...state.currentWorkspace, + ...action.payload, + }; + }, }, }); diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/avatar.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/avatar.ts new file mode 100644 index 0000000000..a9a752c579 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/utils/avatar.ts @@ -0,0 +1,26 @@ +export function stringToColor(string: string) { + let hash = 0; + let i; + + /* eslint-disable no-bitwise */ + for (i = 0; i < string.length; i += 1) { + hash = string.charCodeAt(i) + ((hash << 5) - hash); + } + + let color = '#'; + + for (i = 0; i < 3; i += 1) { + const value = (hash >> (i * 8)) & 0xff; + + color += `00${value.toString(16)}`.slice(-2); + } + /* eslint-enable no-bitwise */ + + return color; +} + +export function stringToShortName(string: string) { + const [firstName, lastName = ''] = string.split(' '); + + return `${firstName.charAt(0)}${lastName.charAt(0)}`; +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/mui.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/mui.ts index badd5c60a3..94e2cf94d5 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/mui.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/utils/mui.ts @@ -56,7 +56,10 @@ export const getDesignTokens = (isDark: boolean): ThemeOptions => { }, outlinedInherit: { color: 'var(--text-title)', - borderColor: 'var(--line-divider)', + borderColor: 'var(--line-border)', + '&:hover': { + boxShadow: 'var(--shadow)', + }, }, }, }, diff --git a/frontend/appflowy_tauri/vite.config.ts b/frontend/appflowy_tauri/vite.config.ts index a0153b9276..b571cc40de 100644 --- a/frontend/appflowy_tauri/vite.config.ts +++ b/frontend/appflowy_tauri/vite.config.ts @@ -33,7 +33,7 @@ export default defineConfig({ }, }), ], - publicDir: '../appflowy_flutter/assets', + // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` // prevent vite from obscuring rust errors clearScreen: false, diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index fb50bf0998..8481650cf8 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -257,7 +257,14 @@ "remove": "Remove", "dontRemove": "Don't remove", "copyLink": "Copy Link", - "align": "Align" + "align": "Align", + "login": "Login", + "logout": "Log out", + "deleteAccount": "Delete account", + "back": "Back", + "signInGoogle": "Sign in with Google", + "signInGithub": "Sign in with Github", + "signInDiscord": "Sign in with Discord" }, "label": { "welcome": "Welcome!", @@ -1356,5 +1363,47 @@ "addField": "Add field", "userIcon": "User icon" }, - "noLogFiles": "There're no log files" + "noLogFiles": "There're no log files", + "newSettings": { + "myAccount": { + "title": "My account", + "subtitle": "Customize your profile, manage account security, open AI keys, or login into your account.", + "profileLabel": "Account name & Profile image", + "profileNamePlaceholder": "Enter your name", + "accountSecurity": "Account security", + "2FA": "2-Step Authentication", + "aiKeys": "AI keys", + "accountLogin": "Account Login", + "updateNameError": "Failed to update name", + "updateIconError": "Failed to update icon", + "deleteAccount": { + "title": "Delete Account", + "subtitle": "Permanently delete your account and all of your data.", + "deleteMyAccount": "Delete my account", + "dialogTitle": "Delete account", + "dialogContent1": "Are you sure you want to permanently delete your account?", + "dialogContent2": "This action cannot be undone, and will remove access from all teamspaces, erasing your entire account, including private workspaces, and removing you from all shared workspaces." + } + }, + "workplace": { + "name": "Workplace", + "title": "Workplace Settings", + "subtitle": "Customize your workspace appearance, theme, font, text layout, date, time, and language.", + "workplaceName": "Workplace name", + "workplaceNamePlaceholder": "Enter workplace name", + "workplaceIcon": "Workplace icon", + "workplaceIconSubtitle": "Upload an image or use an emoji for your workspace. Icon will show in your sidebar and notifications", + "renameError": "Failed to rename workplace", + "updateIconError": "Failed to update icon", + "appearance": { + "name": "Appearance", + "themeMode": { + "auto": "Auto", + "light": "Light", + "dark": "Dark" + }, + "language": "Language" + } + } + } } \ No newline at end of file