feat: implement magic link sign in (#5036)

This commit is contained in:
Lucas.Xu 2024-04-11 16:33:28 +08:00 committed by GitHub
parent b7b4ea2da1
commit 1597f7d94c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
81 changed files with 599 additions and 473 deletions

View File

@ -32,7 +32,7 @@ void main() {
);
tester.expectToSeeText(LocaleKeys.signIn_loginStartWithAnonymous.tr());
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.expectToSeeHomePageWithGetStartedPage();
// reanme the name of the anon user

View File

@ -18,7 +18,7 @@ void main() {
group('board add row test:', () {
testWidgets('from header', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
@ -61,7 +61,7 @@ void main() {
testWidgets('from footer', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);

View File

@ -15,7 +15,7 @@ void main() {
testWidgets('move row to another group', (tester) async {
const card1Name = 'Card 1';
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
final card1 = find.ancestor(

View File

@ -16,7 +16,7 @@ void main() {
group('board group options:', () {
testWidgets('expand/collapse hidden groups', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
final collapseFinder = find.byFlowySvg(FlowySvgs.pull_left_outlined_s);
@ -45,7 +45,7 @@ void main() {
testWidgets('hide first group, and show it again', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
// Tap the options of the first group
@ -81,7 +81,7 @@ void main() {
testWidgets('delete a group', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
expect(tester.widgetList(find.byType(BoardColumnHeader)).length, 4);

View File

@ -7,8 +7,8 @@ import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import '../../shared/util.dart';
import '../../shared/database_test_op.dart';
import '../../shared/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
@ -16,7 +16,7 @@ void main() {
group('board row test', () {
testWidgets('delete item in ToDo card', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
const name = 'Card 1';
@ -34,7 +34,7 @@ void main() {
testWidgets('duplicate item in ToDo card', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
const name = 'Card 1';
@ -52,7 +52,7 @@ void main() {
testWidgets('add new group', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
// assert number of groups

View File

@ -14,7 +14,7 @@ void main() {
group('calendar', () {
testWidgets('update calendar layout', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(
layout: ViewLayoutPB.Calendar,
@ -36,7 +36,7 @@ void main() {
testWidgets('calendar start from day setting', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// Create calendar view
const name = 'calendar';
@ -69,7 +69,7 @@ void main() {
testWidgets('creating and editing calendar events', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// Create the calendar view
await tester.createNewPageWithNameUnderParent(
@ -153,7 +153,7 @@ void main() {
const customTitle = "EventTitleCustom";
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// Create the calendar view
await tester.createNewPageWithNameUnderParent(
@ -194,7 +194,7 @@ void main() {
testWidgets('rescheduling events', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// Create the calendar view
await tester.createNewPageWithNameUnderParent(

View File

@ -13,7 +13,7 @@ void main() {
group('edit grid cell:', () {
testWidgets('text', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -36,7 +36,7 @@ void main() {
// multiple text cell
testWidgets('multiple text cells', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(
name: 'my grid',
layout: ViewLayoutPB.Grid,
@ -74,7 +74,7 @@ void main() {
testWidgets('number', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -132,7 +132,7 @@ void main() {
testWidgets('checkbox', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -150,7 +150,7 @@ void main() {
testWidgets('created time', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -168,7 +168,7 @@ void main() {
testWidgets('last modified time', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -186,7 +186,7 @@ void main() {
testWidgets('date time', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -282,7 +282,7 @@ void main() {
testWidgets('single select', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
const fieldType = FieldType.SingleSelect;
@ -361,7 +361,7 @@ void main() {
];
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -444,7 +444,7 @@ void main() {
testWidgets('checklist', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);

View File

@ -13,7 +13,7 @@ void main() {
group('database field settings', () {
testWidgets('field visibility', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Grid);

View File

@ -17,7 +17,7 @@ void main() {
group('grid field editor:', () {
testWidgets('rename existing field', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -34,7 +34,7 @@ void main() {
testWidgets('update field type of existing field', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -50,7 +50,7 @@ void main() {
testWidgets('create a field and rename it', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create a new grid
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -65,7 +65,7 @@ void main() {
testWidgets('delete field', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -85,7 +85,7 @@ void main() {
testWidgets('duplicate field', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -105,7 +105,7 @@ void main() {
testWidgets('insert field on either side of a field', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -128,7 +128,7 @@ void main() {
testWidgets('create checklist field', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -148,7 +148,7 @@ void main() {
testWidgets('create list of fields', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -180,7 +180,7 @@ void main() {
testWidgets('field types with empty type option editor', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -212,7 +212,7 @@ void main() {
testWidgets('number field type option', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
await tester.scrollToRight(find.byType(GridPage));
@ -258,7 +258,7 @@ void main() {
testWidgets('add option', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(
layout: ViewLayoutPB.Grid,
@ -286,7 +286,7 @@ void main() {
testWidgets('date time field type options', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
await tester.scrollToRight(find.byType(GridPage));

View File

@ -14,7 +14,7 @@ void main() {
group('reminder in database', () {
testWidgets('add date field and add reminder', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -68,7 +68,7 @@ void main() {
testWidgets('navigate from reminder to open row', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -135,7 +135,7 @@ void main() {
'toggle include time sets reminder option correctly',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(
layout: ViewLayoutPB.Grid,

View File

@ -18,7 +18,7 @@ void main() {
group('grid row detail page:', () {
testWidgets('opens', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// Create a new grid
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -35,7 +35,7 @@ void main() {
testWidgets('add emoji', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// Create a new grid
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -54,7 +54,7 @@ void main() {
testWidgets('update emoji', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// Create a new grid
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -81,7 +81,7 @@ void main() {
testWidgets('remove emoji', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// Create a new grid
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -102,7 +102,7 @@ void main() {
testWidgets('create list of fields', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// Create a new grid
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -136,7 +136,7 @@ void main() {
testWidgets('change order of fields and cells', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// Create a new grid
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -169,7 +169,7 @@ void main() {
testWidgets('hide and show hidden fields', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// Create a new grid
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -218,7 +218,7 @@ void main() {
testWidgets('update the contents of the document and re-open it',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// Create a new grid
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -258,7 +258,7 @@ void main() {
'check if the title wraps properly when a long text is inserted',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// Create a new grid
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -297,7 +297,7 @@ void main() {
testWidgets('delete row', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// Create a new grid
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -314,7 +314,7 @@ void main() {
testWidgets('duplicate row', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// Create a new grid
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);

View File

@ -11,7 +11,7 @@ void main() {
group('grid', () {
testWidgets('create row of the grid', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
await tester.tapCreateRowButtonInGrid();
@ -23,7 +23,7 @@ void main() {
testWidgets('create row from row menu of the grid', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -38,7 +38,7 @@ void main() {
testWidgets('delete row of the grid', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
await tester.hoverOnFirstRowOfGrid();
@ -55,7 +55,7 @@ void main() {
testWidgets('check number of row indicator in the initial grid',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);

View File

@ -12,7 +12,7 @@ void main() {
group('grid', () {
testWidgets('update layout', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -29,7 +29,7 @@ void main() {
testWidgets('update layout multiple times', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);

View File

@ -12,7 +12,7 @@ void main() {
group('database', () {
testWidgets('create linked view', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -33,7 +33,7 @@ void main() {
testWidgets('rename and delete linked view', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -58,7 +58,7 @@ void main() {
testWidgets('delete the last database view', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);

View File

@ -14,7 +14,7 @@ void main() {
group('document alignment', () {
testWidgets('edit alignment in toolbar', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
final selection = Selection.single(
path: [0],
@ -48,7 +48,7 @@ void main() {
testWidgets('edit alignment using shortcut', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// click the first line of the readme
await tester.editor.tapLineOfEditorAt(0);

View File

@ -15,7 +15,7 @@ void main() {
group('paste in codeblock', () {
testWidgets('paste multiple lines in codeblock', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create a new document
await tester.createNewPageWithNameUnderParent();

View File

@ -304,7 +304,7 @@ extension on WidgetTester {
(String, Uint8List?)? image,
}) async {
await initializeAppFlowy();
await tapGoButton();
await tapAnonymousSignInButton();
// create a new document
await createNewPageWithNameUnderParent();

View File

@ -14,7 +14,7 @@ void main() {
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create a new document
await tester.createNewPageWithNameUnderParent();
@ -30,7 +30,7 @@ void main() {
testWidgets('delete the readme page and restore it', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// delete the readme page
await tester.hoverOnPageName(
@ -54,7 +54,7 @@ void main() {
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// delete the readme page
await tester.hoverOnPageName(

View File

@ -15,7 +15,7 @@ void main() {
group('insert inline document reference', () {
testWidgets('insert by slash menu', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
final name = await createDocumentToReference(tester);
@ -42,7 +42,7 @@ void main() {
testWidgets('insert by `[[` character shortcut', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
final name = await createDocumentToReference(tester);
@ -62,7 +62,7 @@ void main() {
testWidgets('insert by `+` character shortcut', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
final name = await createDocumentToReference(tester);

View File

@ -12,7 +12,7 @@ void main() {
'click + to add a block after current selection, and click + and option key to add a block before current selection',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
var editorState = tester.editor.getCurrentEditorState();
expect(editorState.getNodeAtPath([1])?.delta?.toPlainText(), isNotEmpty);

View File

@ -13,7 +13,7 @@ void main() {
(tester) async {
// combine the two tests into one to avoid the time-consuming process of initializing the app
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
final selection = Selection.single(
path: [0],

View File

@ -16,7 +16,7 @@ void main() {
group('cover image', () {
testWidgets('document cover tests', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
tester.expectToSeeNoDocumentCover();
@ -53,7 +53,7 @@ void main() {
testWidgets('document icon tests', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
tester.expectToSeeDocumentIcon('⭐️');
@ -86,7 +86,7 @@ void main() {
testWidgets('icon and cover at the same time', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
tester.expectToSeeDocumentIcon('⭐️');
tester.expectToSeeNoDocumentCover();
@ -110,7 +110,7 @@ void main() {
testWidgets('shuffle icon', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.editor.tapGettingStartedIcon();
@ -123,7 +123,7 @@ void main() {
testWidgets('change skin tone', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.editor.tapGettingStartedIcon();

View File

@ -20,7 +20,7 @@ void main() {
group('database view in document', () {
testWidgets('insert a referenced grid', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await insertReferenceDatabase(tester, ViewLayoutPB.Grid);
@ -48,7 +48,7 @@ void main() {
testWidgets('insert a referenced board', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await insertReferenceDatabase(tester, ViewLayoutPB.Board);
@ -64,7 +64,7 @@ void main() {
testWidgets('insert a referenced calendar', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await insertReferenceDatabase(tester, ViewLayoutPB.Calendar);
@ -80,7 +80,7 @@ void main() {
testWidgets('create a grid inside a document', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await createInlineDatabase(tester, ViewLayoutPB.Grid);
@ -96,7 +96,7 @@ void main() {
testWidgets('create a board inside a document', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await createInlineDatabase(tester, ViewLayoutPB.Board);
@ -112,7 +112,7 @@ void main() {
testWidgets('create a calendar inside a document', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await createInlineDatabase(tester, ViewLayoutPB.Calendar);

View File

@ -32,7 +32,7 @@ void main() {
group('image block in document', () {
testWidgets('insert an image from local file', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create a new document
await tester.createNewPageWithNameUnderParent(
@ -80,7 +80,7 @@ void main() {
testWidgets('insert an image from network', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create a new document
await tester.createNewPageWithNameUnderParent(
@ -133,7 +133,7 @@ void main() {
testWidgets('insert an image from unsplash', (tester) async {
await runWithNetworkImages(() async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create a new document
await tester.createNewPageWithNameUnderParent(

View File

@ -16,7 +16,7 @@ void main() {
group('inline math equation in document', () {
testWidgets('insert an inline math equation', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create a new document
await tester.createNewPageWithNameUnderParent(
@ -61,7 +61,7 @@ void main() {
testWidgets('remove the inline math equation format', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create a new document
await tester.createNewPageWithNameUnderParent(

View File

@ -13,7 +13,7 @@ void main() {
group('inline page view in document', () {
testWidgets('insert a inline page - grid', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await insertInlinePage(tester, ViewLayoutPB.Grid);
@ -24,7 +24,7 @@ void main() {
testWidgets('insert a inline page - board', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await insertInlinePage(tester, ViewLayoutPB.Board);
@ -35,7 +35,7 @@ void main() {
testWidgets('insert a inline page - calendar', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await insertInlinePage(tester, ViewLayoutPB.Calendar);
@ -46,7 +46,7 @@ void main() {
testWidgets('insert a inline page - document', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await insertInlinePage(tester, ViewLayoutPB.Document);
@ -57,7 +57,7 @@ void main() {
testWidgets('insert a inline page and rename it', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
final pageName = await insertInlinePage(tester, ViewLayoutPB.Document);
@ -76,7 +76,7 @@ void main() {
testWidgets('insert a inline page and delete it', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
final pageName = await insertInlinePage(tester, ViewLayoutPB.Grid);

View File

@ -20,7 +20,7 @@ void main() {
testWidgets('insert/edit/open link', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create a new document
await tester.createNewPageWithNameUnderParent();

View File

@ -19,7 +19,7 @@ void main() {
group('outline block test', () {
testWidgets('insert an outline block', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(
name: 'outline_test',
@ -35,7 +35,7 @@ void main() {
testWidgets('insert an outline block and check if headings are visible',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(
name: 'outline_test',
@ -91,7 +91,7 @@ void main() {
testWidgets("control the depth of outline block", (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(
name: 'outline_test',

View File

@ -36,7 +36,7 @@ void main() {
testWidgets('convert > to toggle list, and click the icon to close it',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create a new document
await tester.createNewPageWithNameUnderParent();
@ -80,7 +80,7 @@ void main() {
(tester) async {
// if the toggle list is closed, press enter key will insert a new toggle list after it
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create a new document
await tester.createNewPageWithNameUnderParent();
@ -117,7 +117,7 @@ void main() {
testWidgets('press enter key when the toggle list is open', (tester) async {
// if the toggle list is open, press enter key will insert a new paragraph inside it
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create a new document
await tester.createNewPageWithNameUnderParent();
@ -152,7 +152,7 @@ void main() {
testWidgets('clear the format if toggle list if empty', (tester) async {
// if the toggle list is open, press enter key will insert a new paragraph inside it
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create a new document
await tester.createNewPageWithNameUnderParent();
@ -181,7 +181,7 @@ void main() {
(tester) async {
// if the toggle list is open, press enter key will insert a new paragraph inside it
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create a new document
await tester.createNewPageWithNameUnderParent();

View File

@ -13,7 +13,7 @@ void main() {
group('edit document', () {
testWidgets('redo & undo', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create a new document called Sample
const pageName = 'Sample';
@ -67,7 +67,7 @@ void main() {
testWidgets('write a readme document', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create a new document called Sample
const pageName = 'Sample';

View File

@ -13,7 +13,7 @@ void main() {
group('Grid Calculations', () {
testWidgets('add calculation and update cell', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
@ -47,7 +47,7 @@ void main() {
testWidgets('add calculations and remove row', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);

View File

@ -27,7 +27,7 @@ void main() {
const time = "23:59";
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
final dateTimeSettings =
await UserSettingsBackendService().getDateTimeSettings();
@ -76,7 +76,7 @@ void main() {
testWidgets('Add reminder for tomorrow, and navigate to it',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.editor.tapLineOfEditorAt(0);
await tester.editor.getCurrentEditorState().insertNewLine();

View File

@ -11,7 +11,7 @@ void main() {
group('board add row test', () {
testWidgets('Add card from header', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.notifications);

View File

@ -14,7 +14,7 @@ void main() {
testWidgets('select language, language changed', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.expectToSeeHomePageWithGetStartedPage();
await tester.openSettings();

View File

@ -15,7 +15,7 @@ void main() {
group('Rename current view item', () {
testWidgets('by F2 shortcut', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await FlowyTestKeyboard.simulateKeyDownEvent(
[LogicalKeyboardKey.f2],

View File

@ -27,7 +27,7 @@ void main() {
testWidgets('first time the personal folder is expanded', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// first time is expanded
expect(isExpanded(type: FolderCategoryType.private), true);

View File

@ -18,7 +18,7 @@ void main() {
'Toggle favorites for views creates / removes the favorite header along with favorite views',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// no favorite folder
expect(find.byType(FavoriteFolder), findsNothing);
@ -74,7 +74,7 @@ void main() {
'renaming a favorite view updates name under favorite header',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
const name = 'test';
await tester.favoriteViewByName(gettingStarted);
@ -100,7 +100,7 @@ void main() {
'deleting first level favorite view removes its instance from favorite header, deleting root level views leads to removal of all favorites that are its children',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
final names = [1, 2].map((e) => 'document_$e').toList();
for (var i = 0; i < names.length; i++) {
@ -157,7 +157,7 @@ void main() {
'view selection is synced between favorites and personal folder',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent();
await tester.favoriteViewByName(gettingStarted);
@ -177,7 +177,7 @@ void main() {
'context menu opens up for favorites',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent();
await tester.favoriteViewByName(gettingStarted);

View File

@ -14,7 +14,7 @@ void main() {
group('Icon', () {
testWidgets('Update page icon in sidebar', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create document, board, grid and calendar views
for (final value in ViewLayoutPB.values) {
@ -42,7 +42,7 @@ void main() {
testWidgets('Update page icon in title bar', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create document, board, grid and calendar views
for (final value in ViewLayoutPB.values) {

View File

@ -20,7 +20,7 @@ void main() {
group('sidebar test', () {
testWidgets('create a new page', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// create a new page
await tester.tapNewPageButton();
@ -36,7 +36,7 @@ void main() {
testWidgets('create a new document, grid, board and calendar',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
for (final layout in ViewLayoutPB.values) {
// create a new page
@ -74,7 +74,7 @@ void main() {
testWidgets('create some nested pages, and move them', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
final names = [1, 2, 3, 4].map((e) => 'document_$e').toList();
for (var i = 0; i < names.length; i++) {
@ -140,7 +140,7 @@ void main() {
testWidgets('unable to move a document into a database', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
const document = 'document';
await tester.createNewPageWithNameUnderParent(
@ -183,7 +183,7 @@ void main() {
testWidgets('unable to create a new database inside the existing one',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
const grid = 'grid';
await tester.createNewPageWithNameUnderParent(

View File

@ -15,7 +15,7 @@ void main() {
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.expectToSeeHomePageWithGetStartedPage();
await tester.openSettings();
@ -47,7 +47,7 @@ void main() {
testWidgets('reset the font family', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.expectToSeeHomePageWithGetStartedPage();
await tester.openSettings();

View File

@ -17,7 +17,7 @@ void main() {
testWidgets('cmd/ctrl+alt+e shortcut opens the emoji picker',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
final Finder editor = find.byType(AppFlowyEditor);
await tester.tap(editor);

View File

@ -10,7 +10,7 @@ void main() {
group('Empty', () {
testWidgets('toggle theme mode', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
});
});
}

View File

@ -19,7 +19,7 @@ void main() {
testWidgets('toggle theme mode', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.expectToSeeHomePageWithGetStartedPage();
await tester.openSettings();
@ -71,7 +71,7 @@ void main() {
testWidgets('show or hide home menu', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
await tester.expectToSeeHomePageWithGetStartedPage();
await tester.pumpAndSettle();

View File

@ -15,7 +15,7 @@ void main() {
group('import files', () {
testWidgets('import multiple markdown files', (tester) async {
final context = await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// expect to see a getting started page
tester.expectToSeePageName(gettingStarted);
@ -48,7 +48,7 @@ void main() {
testWidgets('import markdown file with table', (tester) async {
final context = await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// expect to see a getting started page
tester.expectToSeePageName(gettingStarted);
@ -77,12 +77,18 @@ void main() {
await tester.openPage('markdown_with_table');
final importedPageEditorState = tester.editor.getCurrentEditorState();
expect(importedPageEditorState.getNodeAtPath([0])!.type,
HeadingBlockKeys.type,);
expect(importedPageEditorState.getNodeAtPath([2])!.type,
HeadingBlockKeys.type,);
expect(importedPageEditorState.getNodeAtPath([4])!.type,
TableBlockKeys.type,);
expect(
importedPageEditorState.getNodeAtPath([0])!.type,
HeadingBlockKeys.type,
);
expect(
importedPageEditorState.getNodeAtPath([2])!.type,
HeadingBlockKeys.type,
);
expect(
importedPageEditorState.getNodeAtPath([4])!.type,
TableBlockKeys.type,
);
});
});
}

View File

@ -14,7 +14,7 @@ void main() {
group('share markdown in document page', () {
testWidgets('click the share button in document page', (tester) async {
final context = await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// mock the file picker
final path = await mockSaveFilePath(
@ -38,7 +38,7 @@ void main() {
'share the markdown after renaming the document name',
(tester) async {
final context = await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// expect to see a getting started page
tester.expectToSeePageName(gettingStarted);
@ -104,12 +104,12 @@ fn main() {
> Click `?` at the bottom right for help and support.
> 🥰
>
>
> Like AppFlowy? Follow us:
> [GitHub](https://github.com/AppFlowy-IO/AppFlowy)
> [Twitter](https://twitter.com/appflowy): @appflowy
> [Newsletter](https://blog-appflowy.ghost.io/)
>
>

View File

@ -94,7 +94,7 @@ void main() {
testWidgets('reset to default location', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
// home and readme document
await tester.expectToSeeHomePageWithGetStartedPage();

View File

@ -22,7 +22,7 @@ void main() {
group('Tabs', () {
testWidgets('Open AppFlowy and open/navigate/close tabs', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
await tester.tapAnonymousSignInButton();
expect(
find.descendant(

View File

@ -118,7 +118,7 @@ extension AppFlowyTestBase on WidgetTester {
Future<void> waitUntilSignInPageShow() async {
if (isAuthEnabled) {
final finder = find.byType(SignInAnonymousButton);
final finder = find.byType(SignInAnonymousButtonV2);
await pumpUntilFound(finder, timeout: const Duration(seconds: 30));
expect(finder, findsOneWidget);
} else {

View File

@ -36,14 +36,14 @@ import 'util.dart';
extension CommonOperations on WidgetTester {
/// Tap the GetStart button on the launch page.
Future<void> tapGoButton() async {
Future<void> tapAnonymousSignInButton() async {
// local version
final goButton = find.byType(GoButton);
if (goButton.evaluate().isNotEmpty) {
await tapButton(goButton);
} else {
// cloud version
final anonymousButton = find.byType(SignInAnonymousButton);
final anonymousButton = find.byType(SignInAnonymousButtonV2);
await tapButton(anonymousButton);
}

View File

@ -98,7 +98,7 @@ import 'mock/mock_file_picker.dart';
extension AppFlowyDatabaseTest on WidgetTester {
Future<void> openV020database() async {
final context = await initializeAppFlowy();
await tapGoButton();
await tapAnonymousSignInButton();
// expect to see a readme page
expectToSeePageName(gettingStarted);

View File

@ -194,7 +194,10 @@ class InitAppFlowyCloudTask extends LaunchTask {
}
class DeepLinkResult {
DeepLinkResult({required this.state, this.result});
DeepLinkResult({
required this.state,
this.result,
});
final DeepLinkState state;
final FlowyResult<UserProfilePB, FlowyError>? result;

View File

@ -100,7 +100,10 @@ class AppFlowyCloudAuthService implements AuthService {
required String email,
Map<String, String> params = const {},
}) async {
throw UnimplementedError();
return _backendAuthService.signInWithMagicLink(
email: email,
params: params,
);
}
@override

View File

@ -72,7 +72,7 @@ abstract class AuthService {
/// - `params`: Additional parameters for authentication with magic link (optional).
///
/// Returns [UserProfilePB] if the user is authenticated, otherwise returns [FlowyError].
Future<FlowyResult<UserProfilePB, FlowyError>> signInWithMagicLink({
Future<FlowyResult<void, FlowyError>> signInWithMagicLink({
required String email,
Map<String, String> params,
});

View File

@ -104,10 +104,7 @@ class BackendAuthService implements AuthService {
required String email,
Map<String, String> params = const {},
}) async {
return FlowyResult.failure(
FlowyError.create()
..code = ErrorCode.Internal
..msg = "Unsupported sign up action",
);
// No need to pass the redirect URL.
return UserBackendService.signInWithMagicLink(email, '');
}
}

View File

@ -7,6 +7,7 @@ import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
show UserProfilePB;
import 'package:appflowy_result/appflowy_result.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@ -23,11 +24,58 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
});
}
_dispatch();
on<SignInEvent>(
(event, emit) async {
await event.when(
signedInWithUserEmailAndPassword: () async => _onSignIn(emit),
signedInWithOAuth: (platform) async => _onSignInWithOAuth(
emit,
platform,
),
signedInAsGuest: () async => _onSignInAsGuest(emit),
signedWithMagicLink: (email) async => _onSignInWithMagicLink(
emit,
email,
),
deepLinkStateChange: (result) => _onDeepLinkStateChange(
emit,
result,
),
cancel: () {
emit(
state.copyWith(
isSubmitting: false,
emailError: null,
passwordError: null,
successOrFail: null,
),
);
},
emailChanged: (email) async {
emit(
state.copyWith(
email: email,
emailError: null,
successOrFail: null,
),
);
},
passwordChanged: (password) async {
emit(
state.copyWith(
password: password,
passwordError: null,
successOrFail: null,
),
);
},
);
},
);
}
final AuthService authService;
void Function()? deepLinkStateListener;
VoidCallback? deepLinkStateListener;
@override
Future<void> close() {
@ -40,87 +88,39 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
return super.close();
}
void _dispatch() {
on<SignInEvent>(
(event, emit) async {
await event.map(
signedInWithUserEmailAndPassword: (e) async {
await _performActionOnSignIn(
state,
emit,
);
},
signedInWithOAuth: (value) async =>
_performActionOnSignInWithOAuth(state, emit, value.platform),
signedInAsGuest: (value) async =>
_performActionOnSignInAsGuest(state, emit),
emailChanged: (EmailChanged value) async {
emit(
state.copyWith(
email: value.email,
emailError: null,
successOrFail: null,
),
);
},
passwordChanged: (PasswordChanged value) async {
emit(
state.copyWith(
password: value.password,
passwordError: null,
successOrFail: null,
),
);
},
signedWithMagicLink: (SignedWithMagicLink value) async {
await _performActionOnSignInWithMagicLink(state, emit, value.email);
},
deepLinkStateChange: (_DeepLinkStateChange value) {
final deepLinkState = value.result.state;
Future<void> _onDeepLinkStateChange(
Emitter<SignInState> emit,
DeepLinkResult result,
) async {
final deepLinkState = result.state;
switch (deepLinkState) {
case DeepLinkState.none:
break;
case DeepLinkState.loading:
emit(
state.copyWith(
isSubmitting: true,
emailError: null,
passwordError: null,
successOrFail: null,
),
);
case DeepLinkState.finish:
if (value.result.result != null) {
emit(
value.result.result!.fold(
(userProfile) => state.copyWith(
isSubmitting: false,
successOrFail: FlowyResult.success(userProfile),
),
(error) => stateFromCode(error),
),
);
}
}
},
cancel: (value) {
emit(
state.copyWith(
isSubmitting: false,
emailError: null,
passwordError: null,
successOrFail: null,
),
);
},
switch (deepLinkState) {
case DeepLinkState.none:
break;
case DeepLinkState.loading:
emit(
state.copyWith(
isSubmitting: true,
emailError: null,
passwordError: null,
successOrFail: null,
),
);
},
);
case DeepLinkState.finish:
final newState = result.result?.fold(
(s) => state.copyWith(
isSubmitting: false,
successOrFail: FlowyResult.success(s),
),
(f) => _stateFromCode(f),
);
if (newState != null) {
emit(newState);
}
}
}
Future<void> _performActionOnSignIn(
SignInState state,
Future<void> _onSignIn(
Emitter<SignInState> emit,
) async {
final result = await authService.signInWithEmailPassword(
@ -133,13 +133,12 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
isSubmitting: false,
successOrFail: FlowyResult.success(userProfile),
),
(error) => stateFromCode(error),
(error) => _stateFromCode(error),
),
);
}
Future<void> _performActionOnSignInWithOAuth(
SignInState state,
Future<void> _onSignInWithOAuth(
Emitter<SignInState> emit,
String platform,
) async {
@ -161,13 +160,12 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
isSubmitting: false,
successOrFail: FlowyResult.success(userProfile),
),
(error) => stateFromCode(error),
(error) => _stateFromCode(error),
),
);
}
Future<void> _performActionOnSignInWithMagicLink(
SignInState state,
Future<void> _onSignInWithMagicLink(
Emitter<SignInState> emit,
String email,
) async {
@ -187,16 +185,14 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
emit(
result.fold(
(userProfile) => state.copyWith(
isSubmitting: false,
successOrFail: FlowyResult.success(userProfile),
isSubmitting: true,
),
(error) => stateFromCode(error),
(error) => _stateFromCode(error),
),
);
}
Future<void> _performActionOnSignInAsGuest(
SignInState state,
Future<void> _onSignInAsGuest(
Emitter<SignInState> emit,
) async {
emit(
@ -215,12 +211,12 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
isSubmitting: false,
successOrFail: FlowyResult.success(userProfile),
),
(error) => stateFromCode(error),
(error) => _stateFromCode(error),
),
);
}
SignInState stateFromCode(FlowyError error) {
SignInState _stateFromCode(FlowyError error) {
switch (error.code) {
case ErrorCode.EmailFormatInvalid:
return state.copyWith(
@ -255,7 +251,7 @@ class SignInEvent with _$SignInEvent {
const factory SignInEvent.emailChanged(String email) = EmailChanged;
const factory SignInEvent.passwordChanged(String password) = PasswordChanged;
const factory SignInEvent.deepLinkStateChange(DeepLinkResult result) =
_DeepLinkStateChange;
DeepLinkStateChange;
const factory SignInEvent.cancel() = _Cancel;
}

View File

@ -63,6 +63,14 @@ class UserBackendService {
throw UnimplementedError();
}
static Future<FlowyResult<UserProfilePB, FlowyError>> signInWithMagicLink(
String email,
String redirectTo,
) async {
final payload = MagicLinkSignInPB(email: email, redirectTo: redirectTo);
return UserEventMagicLinkSignIn(payload).send();
}
static Future<FlowyResult<void, FlowyError>> signOut() {
return UserEventSignOut().send();
}

View File

@ -1,6 +1,7 @@
import 'package:appflowy/core/frameless_window.dart';
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/magic_link_sign_in_buttons.dart';
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
import 'package:appflowy/user/presentation/widgets/widgets.dart';
import 'package:easy_localization/easy_localization.dart';
@ -28,22 +29,9 @@ class DesktopSignInScreen extends StatelessWidget {
logoSize: const Size(60, 60),
),
const VSpace(30),
// Email and password. don't support yet.
/*
...[
const EmailTextField(),
const VSpace(5),
const PasswordTextField(),
const VSpace(20),
const LoginButton(),
const VSpace(10),
const VSpace(10),
SignUpPrompt(router: router),
],
*/
const SignInAnonymousButton(),
// const SignInAnonymousButton(),
const SignInWithMagicLinkButtons(),
// third-party sign in.
const VSpace(20),
@ -54,6 +42,10 @@ class DesktopSignInScreen extends StatelessWidget {
const ThirdPartySignInButtons(),
],
const VSpace(20),
// anonymous sign in
const SignInAnonymousButtonV2(),
// loading status
const VSpace(indicatorMinHeight),
isLoading
@ -61,18 +53,6 @@ class DesktopSignInScreen extends StatelessWidget {
minHeight: indicatorMinHeight,
)
: const VSpace(indicatorMinHeight),
// add the same space when there's no loading status.
// ConstrainedBox(
// constraints: const BoxConstraints(maxHeight: 140),
// child: HistoricalUserList(
// didOpenUser: () async {
// await FlowyRunner.run(
// FlowyApp(),
// integrationEnv(),
// );
// },
// ),
// ),
const VSpace(20),
],
),
@ -106,136 +86,3 @@ class _OrDivider extends StatelessWidget {
);
}
}
// The following code is migrated from previous signInScreen.dart(for desktop)
// We may need this later when sign up&in feature is ready
// class SignUpPrompt extends StatelessWidget {
// const SignUpPrompt({
// super.key,
// required this.router,
// }) ;
// final AuthRouter router;
// @override
// Widget build(BuildContext context) {
// return Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// FlowyText.medium(
// LocaleKeys.signIn_dontHaveAnAccount.tr(),
// color: Theme.of(context).hintColor,
// ),
// TextButton(
// style: TextButton.styleFrom(
// textStyle: Theme.of(context).textTheme.bodyMedium,
// ),
// onPressed: () => router.pushSignUpScreen(context),
// child: Text(
// LocaleKeys.signUp_buttonText.tr(),
// style: TextStyle(color: Theme.of(context).colorScheme.primary),
// ),
// ),
// ForgetPasswordButton(router: router),
// ],
// );
// }
// }
// class LoginButton extends StatelessWidget {
// const LoginButton({
// super.key
// }) ;
// @override
// Widget build(BuildContext context) {
// return RoundedTextButton(
// title: LocaleKeys.signIn_loginButtonText.tr(),
// height: 48,
// borderRadius: Corners.s10Border,
// onPressed: () => context
// .read<SignInBloc>()
// .add(const SignInEvent.signedInWithUserEmailAndPassword()),
// );
// }
// }
// class ForgetPasswordButton extends StatelessWidget {
// const ForgetPasswordButton({
// super.key
// required this.router,
// }) ;
// final AuthRouter router;
// @override
// Widget build(BuildContext context) {
// return TextButton(
// style: TextButton.styleFrom(
// textStyle: Theme.of(context).textTheme.bodyMedium,
// ),
// onPressed: () {
// throw UnimplementedError();
// },
// child: Text(
// LocaleKeys.signIn_forgotPassword.tr(),
// style: TextStyle(color: Theme.of(context).colorScheme.primary),
// ),
// );
// }
// }
// class PasswordTextField extends StatelessWidget {
// const PasswordTextField({
// super.key
// }) ;
// @override
// Widget build(BuildContext context) {
// return BlocBuilder<SignInBloc, SignInState>(
// buildWhen: (previous, current) =>
// previous.passwordError != current.passwordError,
// builder: (context, state) {
// return RoundedInputField(
// obscureText: true,
// obscureIcon: const FlowySvg(FlowySvgs.hide_m),
// obscureHideIcon: const FlowySvg(FlowySvgs.show_m),
// hintText: LocaleKeys.signIn_passwordHint.tr(),
// errorText: context
// .read<SignInBloc>()
// .state
// .passwordError
// .fold(() => "", (error) => error),
// onChanged: (value) => context
// .read<SignInBloc>()
// .add(SignInEvent.passwordChanged(value)),
// );
// },
// );
// }
// }
// class EmailTextField extends StatelessWidget {
// const EmailTextField({
// super.key
// }) ;
// @override
// Widget build(BuildContext context) {
// return BlocBuilder<SignInBloc, SignInState>(
// buildWhen: (previous, current) =>
// previous.emailError != current.emailError,
// builder: (context, state) {
// return RoundedInputField(
// hintText: LocaleKeys.signIn_emailHint.tr(),
// errorText: context
// .read<SignInBloc>()
// .state
// .emailError
// .fold(() => "", (error) => error),
// onChanged: (value) =>
// context.read<SignInBloc>().add(SignInEvent.emailChanged(value)),
// );
// },
// );
// }
// }

View File

@ -2,6 +2,7 @@ import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/setting/launch_settings_page.dart';
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/magic_link_sign_in_buttons.dart';
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@ -27,15 +28,14 @@ class MobileSignInScreen extends StatelessWidget {
const VSpace(spacing * 2),
_buildWelcomeText(),
_buildAppNameText(colorScheme),
const VSpace(spacing * 2),
const SignInWithMagicLinkButtons(),
const VSpace(spacing),
const Spacer(flex: 2),
const SignInAnonymousButton(),
if (isAuthEnabled) _buildThirdPartySignInButtons(colorScheme),
const VSpace(spacing),
if (isAuthEnabled) ...[
_buildThirdPartySignInButtons(colorScheme),
const VSpace(spacing),
_buildSettingsButton(context),
],
const SignInAnonymousButtonV2(),
const VSpace(spacing),
_buildSettingsButton(context),
if (!isAuthEnabled) const Spacer(flex: 2),
],
),

View File

@ -0,0 +1,109 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/user/application/sign_in_bloc.dart';
import 'package:appflowy/workspace/presentation/home/toast.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:string_validator/string_validator.dart';
class SignInWithMagicLinkButtons extends StatefulWidget {
const SignInWithMagicLinkButtons({
super.key,
});
@override
State<SignInWithMagicLinkButtons> createState() =>
_SignInWithMagicLinkButtonsState();
}
class _SignInWithMagicLinkButtonsState
extends State<SignInWithMagicLinkButtons> {
final controller = TextEditingController();
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 48.0,
child: FlowyTextField(
controller: controller,
hintText: LocaleKeys.signIn_pleaseInputYourEmail.tr(),
onSubmitted: (_) => _sendMagicLink(context, controller.text),
),
),
const VSpace(12),
_ConfirmButton(
onTap: () => _sendMagicLink(context, controller.text),
),
],
);
}
void _sendMagicLink(BuildContext context, String email) {
if (!isEmail(email)) {
showSnackBarMessage(
context,
LocaleKeys.signIn_invalidEmail.tr(),
duration: const Duration(seconds: 8),
);
return;
}
context.read<SignInBloc>().add(SignInEvent.signedWithMagicLink(email));
showSnackBarMessage(
context,
LocaleKeys.signIn_magicLinkSent.tr(),
duration: const Duration(seconds: 1000),
);
}
}
class _ConfirmButton extends StatelessWidget {
const _ConfirmButton({
required this.onTap,
});
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
if (PlatformExtension.isMobile) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
minimumSize: const Size(double.infinity, 56),
),
onPressed: onTap,
child: FlowyText(
LocaleKeys.signIn_logInWithMagicLink.tr(),
fontSize: 14,
color: Theme.of(context).colorScheme.onPrimary,
fontWeight: FontWeight.w500,
),
);
} else {
return SizedBox(
height: 48,
child: FlowyButton(
isSelected: true,
onTap: onTap,
hoverColor: Theme.of(context).colorScheme.primary,
text: FlowyText.medium(
LocaleKeys.signIn_logInWithMagicLink.tr(),
textAlign: TextAlign.center,
),
radius: Corners.s6Border,
),
);
}
}
}

View File

@ -9,8 +9,8 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
/// Used in DesktopSignInScreen and MobileSignInScreen
class SignInAnonymousButton extends StatelessWidget {
/// Used in DesktopSignInScreen and MobileSignInScreen
const SignInAnonymousButton({
super.key,
});
@ -85,3 +85,59 @@ class SignInAnonymousButton extends StatelessWidget {
);
}
}
class SignInAnonymousButtonV2 extends StatelessWidget {
const SignInAnonymousButtonV2({
super.key,
});
@override
Widget build(BuildContext context) {
return BlocBuilder<SignInBloc, SignInState>(
builder: (context, signInState) {
return BlocProvider(
create: (context) => AnonUserBloc()
..add(
const AnonUserEvent.initial(),
),
child: BlocListener<AnonUserBloc, AnonUserState>(
listener: (context, state) async {
if (state.openedAnonUser != null) {
await runAppFlowy();
}
},
child: BlocBuilder<AnonUserBloc, AnonUserState>(
builder: (context, state) {
final text = state.anonUsers.isEmpty
? LocaleKeys.signIn_loginStartWithAnonymous.tr()
: LocaleKeys.signIn_continueAnonymousUser.tr();
final onTap = state.anonUsers.isEmpty
? () {
context
.read<SignInBloc>()
.add(const SignInEvent.signedInAsGuest());
}
: () {
final bloc = context.read<AnonUserBloc>();
final user = bloc.state.anonUsers.first;
bloc.add(AnonUserEvent.openAnonUser(user));
};
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: onTap,
child: FlowyText(
text,
color: Colors.blue,
fontSize: 12,
),
),
);
},
),
),
);
},
);
}
}

View File

@ -1,5 +1,6 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
@ -53,10 +54,11 @@ void showSnackBarMessage(
BuildContext context,
String message, {
bool showCancel = false,
Duration duration = const Duration(seconds: 4),
}) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Theme.of(context).colorScheme.onSecondary,
backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
action: !showCancel
? null
: SnackBarAction(
@ -68,8 +70,8 @@ void showSnackBarMessage(
),
content: FlowyText(
message,
color: Colors.white,
maxLines: 2,
fontSize: PlatformExtension.isDesktop ? 14 : 12,
),
),
);

View File

@ -11,6 +11,8 @@ const uint8_t *sync_event(const uint8_t *input, uintptr_t len);
int32_t set_stream_port(int64_t port);
int32_t set_log_stream_port(int64_t port);
void link_me_please(void);
void rust_log(int64_t level, const char *data);

View File

@ -1,10 +1,9 @@
import 'dart:async';
import 'package:flowy_infra/size.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flowy_infra/size.dart';
class FlowyTextField extends StatefulWidget {
final String? hintText;
final String? text;

View File

@ -156,7 +156,7 @@ checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "app-error"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"bincode",
@ -712,7 +712,7 @@ dependencies = [
[[package]]
name = "client-api"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"again",
"anyhow",
@ -757,7 +757,7 @@ dependencies = [
[[package]]
name = "client-websocket"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"futures-channel",
"futures-util",
@ -996,7 +996,7 @@ dependencies = [
[[package]]
name = "collab-rt-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"bincode",
@ -1021,7 +1021,7 @@ dependencies = [
[[package]]
name = "collab-rt-protocol"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"bincode",
@ -1258,7 +1258,7 @@ dependencies = [
"cssparser-macros",
"dtoa-short",
"itoa 1.0.6",
"phf 0.8.0",
"phf 0.11.2",
"smallvec",
]
@ -1369,7 +1369,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]]
name = "database-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"app-error",
@ -2679,7 +2679,7 @@ dependencies = [
[[package]]
name = "gotrue"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"futures-util",
@ -2696,7 +2696,7 @@ dependencies = [
[[package]]
name = "gotrue-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"app-error",
@ -3118,7 +3118,7 @@ dependencies = [
[[package]]
name = "infra"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"reqwest",
@ -5518,7 +5518,7 @@ dependencies = [
[[package]]
name = "shared-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"app-error",

View File

@ -87,7 +87,7 @@ yrs = { git = "https://github.com/appflowy/y-crdt", rev = "3f25bb510ca5274e7657d
# Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "b1c3d779badcd382e3d6e46c292042de70d38cdd" }
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e" }
# Please use the following script to update collab.
# Working directory: frontend
#

View File

@ -215,7 +215,7 @@ checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "app-error"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"bincode",
@ -541,7 +541,7 @@ dependencies = [
[[package]]
name = "client-api"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"again",
"anyhow",
@ -586,7 +586,7 @@ dependencies = [
[[package]]
name = "client-websocket"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"futures-channel",
"futures-util",
@ -764,7 +764,7 @@ dependencies = [
[[package]]
name = "collab-rt-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"bincode",
@ -789,7 +789,7 @@ dependencies = [
[[package]]
name = "collab-rt-protocol"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"bincode",
@ -955,7 +955,7 @@ dependencies = [
"cssparser-macros",
"dtoa-short",
"itoa",
"phf 0.8.0",
"phf 0.11.2",
"smallvec",
]
@ -1000,7 +1000,7 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
[[package]]
name = "database-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"app-error",
@ -1762,7 +1762,7 @@ dependencies = [
[[package]]
name = "gotrue"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"futures-util",
@ -1779,7 +1779,7 @@ dependencies = [
[[package]]
name = "gotrue-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"app-error",
@ -2080,7 +2080,7 @@ dependencies = [
[[package]]
name = "infra"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"reqwest",
@ -2763,7 +2763,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
dependencies = [
"phf_macros",
"phf_macros 0.8.0",
"phf_shared 0.8.0",
"proc-macro-hack",
]
@ -2783,6 +2783,7 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_macros 0.11.2",
"phf_shared 0.11.2",
]
@ -2850,6 +2851,19 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "phf_macros"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
dependencies = [
"phf_generator 0.11.2",
"phf_shared 0.11.2",
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "phf_shared"
version = "0.8.0"
@ -3705,7 +3719,7 @@ dependencies = [
[[package]]
name = "shared-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"app-error",

View File

@ -55,7 +55,7 @@ codegen-units = 1
# Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "b1c3d779badcd382e3d6e46c292042de70d38cdd" }
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e" }
# Please use the following script to update collab.
# Working directory: frontend
#

View File

@ -147,7 +147,7 @@ checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
[[package]]
name = "app-error"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"bincode",
@ -687,7 +687,7 @@ dependencies = [
[[package]]
name = "client-api"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"again",
"anyhow",
@ -732,7 +732,7 @@ dependencies = [
[[package]]
name = "client-websocket"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"futures-channel",
"futures-util",
@ -980,7 +980,7 @@ dependencies = [
[[package]]
name = "collab-rt-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"bincode",
@ -1005,7 +1005,7 @@ dependencies = [
[[package]]
name = "collab-rt-protocol"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"bincode",
@ -1246,7 +1246,7 @@ dependencies = [
"cssparser-macros",
"dtoa-short",
"itoa 1.0.10",
"phf 0.11.2",
"phf 0.8.0",
"smallvec",
]
@ -1357,7 +1357,7 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
[[package]]
name = "database-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"app-error",
@ -2749,7 +2749,7 @@ dependencies = [
[[package]]
name = "gotrue"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"futures-util",
@ -2766,7 +2766,7 @@ dependencies = [
[[package]]
name = "gotrue-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"app-error",
@ -3193,7 +3193,7 @@ dependencies = [
[[package]]
name = "infra"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"reqwest",
@ -5609,7 +5609,7 @@ dependencies = [
[[package]]
name = "shared-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"app-error",

View File

@ -87,7 +87,7 @@ yrs = { git = "https://github.com/appflowy/y-crdt", rev = "3f25bb510ca5274e7657d
# Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "b1c3d779badcd382e3d6e46c292042de70d38cdd" }
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e" }
# Please use the following script to update collab.
# Working directory: frontend
#

View File

@ -50,7 +50,11 @@
"LogInWithGithub": "Log in with Github",
"LogInWithDiscord": "Log in with Discord",
"signInWith": "Sign in with:",
"signInWithEmail": "Sign in with Email"
"signInWithEmail": "Sign in with Email",
"logInWithMagicLink": "Log in with Magic Link",
"pleaseInputYourEmail": "Please enter your email address",
"magicLinkSent": "Magic link sent to your email, please check your inbox",
"invalidEmail": "Please enter a valid email address"
},
"workspace": {
"chooseWorkspace": "Choose your workspace",

View File

@ -157,7 +157,7 @@ checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "app-error"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"bincode",
@ -669,7 +669,7 @@ dependencies = [
[[package]]
name = "client-api"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"again",
"anyhow",
@ -714,7 +714,7 @@ dependencies = [
[[package]]
name = "client-websocket"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"futures-channel",
"futures-util",
@ -922,7 +922,7 @@ dependencies = [
[[package]]
name = "collab-rt-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"bincode",
@ -947,7 +947,7 @@ dependencies = [
[[package]]
name = "collab-rt-protocol"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"bincode",
@ -1292,7 +1292,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]]
name = "database-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"app-error",
@ -2481,7 +2481,7 @@ dependencies = [
[[package]]
name = "gotrue"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"futures-util",
@ -2498,7 +2498,7 @@ dependencies = [
[[package]]
name = "gotrue-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"app-error",
@ -2859,7 +2859,7 @@ dependencies = [
[[package]]
name = "infra"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"reqwest",
@ -4873,7 +4873,7 @@ dependencies = [
[[package]]
name = "shared-entity"
version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b1c3d779badcd382e3d6e46c292042de70d38cdd#b1c3d779badcd382e3d6e46c292042de70d38cdd"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e#dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e"
dependencies = [
"anyhow",
"app-error",

View File

@ -111,7 +111,7 @@ rocksdb = { git = "https://github.com/LucasXu0/rust-rocksdb", rev = "21cf4a23ec1
# Run the script.add_workspace_members:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "b1c3d779badcd382e3d6e46c292042de70d38cdd" }
client-api = { git = " https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "dd9cc8d4c78c6d9bfeb9bd7d413222f643c7969e" }
# Please use the following script to update collab.
# Working directory: frontend
#

View File

@ -122,6 +122,23 @@ where
})
}
fn sign_in_with_magic_link(
&self,
email: &str,
redirect_to: &str,
) -> FutureResult<(), FlowyError> {
let email = email.to_owned();
let redirect_to = redirect_to.to_owned();
let try_get_client = self.server.try_get_client();
FutureResult::new(async move {
let client = try_get_client?;
client
.sign_in_with_magic_link(&email, Some(redirect_to))
.await?;
Ok(())
})
}
fn generate_oauth_url_with_provider(&self, provider: &str) -> FutureResult<String, FlowyError> {
let provider = AuthProvider::from(provider);
let try_get_client = self.server.try_get_client();

View File

@ -107,6 +107,16 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
})
}
fn sign_in_with_magic_link(
&self,
_email: &str,
_redirect_to: &str,
) -> FutureResult<(), FlowyError> {
FutureResult::new(async {
Err(FlowyError::local_version_not_support().with_context("Not support"))
})
}
fn generate_oauth_url_with_provider(&self, _provider: &str) -> FutureResult<String, FlowyError> {
FutureResult::new(async {
Err(FlowyError::internal().with_context("Can't oauth url when using offline mode"))

View File

@ -187,6 +187,18 @@ where
})
}
fn sign_in_with_magic_link(
&self,
_email: &str,
_redirect_to: &str,
) -> FutureResult<(), FlowyError> {
FutureResult::new(async {
Err(
FlowyError::not_support().with_context("Can't sign in with magic link when using supabase"),
)
})
}
fn generate_oauth_url_with_provider(&self, _provider: &str) -> FutureResult<String, FlowyError> {
FutureResult::new(async {
Err(FlowyError::internal().with_context("Can't generate oauth url when using supabase"))

View File

@ -142,6 +142,9 @@ pub trait UserCloudService: Send + Sync + 'static {
password: &str,
) -> FutureResult<UserProfile, FlowyError>;
fn sign_in_with_magic_link(&self, email: &str, redirect_to: &str)
-> FutureResult<(), FlowyError>;
/// When the user opens the OAuth URL, it redirects to the corresponding provider's OAuth web page.
/// After the user is authenticated, the browser will open a deep link to the AppFlowy app (iOS, macOS, etc.),
/// which will call [Client::sign_in_with_url]generate_sign_in_url_with_email to sign in.

View File

@ -77,6 +77,15 @@ impl TryInto<SignUpParams> for SignUpPayloadPB {
}
}
#[derive(ProtoBuf, Default)]
pub struct MagicLinkSignInPB {
#[pb(index = 1)]
pub email: String,
#[pb(index = 2)]
pub redirect_to: String,
}
#[derive(ProtoBuf, Default)]
pub struct OauthSignInPB {
/// Use this field to store the third party auth information.

View File

@ -288,6 +288,19 @@ pub async fn get_user_setting(
data_result_ok(user_setting)
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub async fn sign_in_with_magic_link_handler(
data: AFPluginData<MagicLinkSignInPB>,
manager: AFPluginState<Weak<UserManager>>,
) -> Result<(), FlowyError> {
let manager = upgrade_manager(manager)?;
let params = data.into_inner();
manager
.sign_in_with_magic_link(&params.email, &params.redirect_to)
.await?;
Ok(())
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub async fn oauth_sign_in_handler(
data: AFPluginData<OauthSignInPB>,

View File

@ -23,6 +23,7 @@ pub fn init(user_manager: Weak<UserManager>) -> AFPlugin {
.state(user_manager)
.state(store_preferences)
.event(UserEvent::SignInWithEmailPassword, sign_in_with_email_password_handler)
.event(UserEvent::MagicLinkSignIn, sign_in_with_magic_link_handler)
.event(UserEvent::SignUp, sign_up)
.event(UserEvent::InitUser, init_user_handler)
.event(UserEvent::GetUserProfile, get_user_profile_handler)
@ -226,6 +227,9 @@ pub enum UserEvent {
#[event(input = "AcceptWorkspaceInvitationPB")]
AcceptWorkspaceInvitation = 49,
#[event(input = "MagicLinkSignInPB", output = "UserProfilePB")]
MagicLinkSignIn = 50,
}
pub trait UserStatusCallback: Send + Sync + 'static {

View File

@ -672,6 +672,18 @@ impl UserManager {
Ok(url)
}
pub(crate) async fn sign_in_with_magic_link(
&self,
email: &str,
redirect_to: &str,
) -> Result<(), FlowyError> {
let auth_service = self.cloud_services.get_user_service()?;
auth_service
.sign_in_with_magic_link(email, redirect_to)
.await
.map_err(|err| FlowyError::server_error().with_context(err))
}
pub(crate) async fn generate_oauth_url(
&self,
oauth_provider: &str,