Skip to content

Commit cc690bd

Browse files
committed
feat: support ai chat
1 parent 75a5a4d commit cc690bd

40 files changed

+1748
-432
lines changed

deploy/nginx.conf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ http {
8585
root /usr/share/nginx/html;
8686
expires 30d;
8787
access_log off;
88+
89+
add_header 'Access-Control-Allow-Origin' '*' always;
90+
add_header 'Access-Control-Allow-Methods' 'GET' always;
8891
}
8992

9093
location /.well-known/apple-app-site-association {

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"coverage": "pnpm run test:unit && pnpm run test:components"
1919
},
2020
"dependencies": {
21-
"@appflowyinc/editor": "^0.0.40",
21+
"@appflowyinc/ai-chat": "0.0.14",
22+
"@appflowyinc/editor": "^0.1.5",
2223
"@atlaskit/primitives": "^5.5.3",
2324
"@emoji-mart/data": "^1.1.2",
2425
"@emoji-mart/react": "^1.1.1",

pnpm-lock.yaml

Lines changed: 470 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/@types/translations/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2695,7 +2695,8 @@
26952695
"error": {
26962696
"pageNameIsEmpty": "The page name is empty, please try another one"
26972697
},
2698-
"visitOurWebsite": "Visit our official website"
2698+
"visitOurWebsite": "Visit our official website",
2699+
"addMessagesToPage": "Add messages to page"
26992700
},
27002701
"globalComment": {
27012702
"comments": "Comments",

src/application/services/js-services/http/http_api.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ export * from './gotrue';
5151

5252
let axiosInstance: AxiosInstance | null = null;
5353

54+
export function getAxiosInstance() {
55+
return axiosInstance;
56+
}
57+
5458
export function initAPIService(config: AFCloudConfig) {
5559
if(axiosInstance) {
5660
return;

src/application/services/js-services/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ export class AFClientService implements AFService {
7272
APIService.initAPIService(config.cloudConfig);
7373
}
7474

75+
getAxiosInstance() {
76+
return APIService.getAxiosInstance();
77+
}
78+
7579
getClientId() {
7680
return this.clientId;
7781
}

src/application/services/js-services/sync.ts

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export class SyncManager {
1717

1818
private isSending = false;
1919

20-
constructor (private doc: Y.Doc, private context: {
20+
constructor(private doc: Y.Doc, private context: {
2121
userId: string, workspaceId: string, objectId: string, collabType: Types
2222
}) {
2323
this.versionVector = this.loadVersionVector();
@@ -26,41 +26,41 @@ export class SyncManager {
2626
this.setupListener();
2727
}
2828

29-
private setupListener () {
29+
private setupListener() {
3030
this.doc.on('update', (_update: Uint8Array, origin: CollabOrigin) => {
31-
if (origin === CollabOrigin.Remote) return;
31+
if(origin === CollabOrigin.Remote) return;
3232
console.log('Local changes detected. Sending update...', origin);
3333
this.debouncedSendUpdate();
3434
});
3535
}
3636

37-
private getStorageKey (baseKey: string): string {
37+
private getStorageKey(baseKey: string): string {
3838
return `${this.context.userId}_${baseKey}_${this.context.workspaceId}_${this.context.objectId}`;
3939
}
4040

41-
private loadVersionVector (): number {
41+
private loadVersionVector(): number {
4242
const storedVector = localStorage.getItem(this.getStorageKey(VERSION_VECTOR_KEY));
4343

4444
return storedVector ? parseInt(storedVector, 10) : 0;
4545
}
4646

47-
private saveVersionVector () {
47+
private saveVersionVector() {
4848
localStorage.setItem(this.getStorageKey(VERSION_VECTOR_KEY), this.versionVector.toString());
4949
}
5050

51-
private loadUnsyncedFlag (): boolean {
51+
private loadUnsyncedFlag(): boolean {
5252
return localStorage.getItem(this.getStorageKey(UNSYNCED_FLAG_KEY)) === 'true';
5353
}
5454

55-
private saveUnsyncedFlag () {
55+
private saveUnsyncedFlag() {
5656
localStorage.setItem(this.getStorageKey(UNSYNCED_FLAG_KEY), this.hasUnsyncedChanges.toString());
5757
}
5858

59-
private loadLastSyncedAt (): string {
59+
private loadLastSyncedAt(): string {
6060
return localStorage.getItem(this.getStorageKey(LAST_SYNCED_AT_KEY)) || '';
6161
}
6262

63-
private saveLastSyncedAt () {
63+
private saveLastSyncedAt() {
6464
localStorage.setItem(this.getStorageKey(LAST_SYNCED_AT_KEY), this.lastSyncedAt);
6565
}
6666

@@ -71,8 +71,8 @@ export class SyncManager {
7171
void this.sendUpdate();
7272
}, 1000); // 1 second debounce
7373

74-
private async sendUpdate () {
75-
if (this.isSending) return;
74+
private async sendUpdate() {
75+
if(this.isSending) return;
7676
this.isSending = true;
7777

7878
try {
@@ -85,14 +85,14 @@ export class SyncManager {
8585

8686
const response = await updateCollab(this.context.workspaceId, this.context.objectId, this.context.collabType, update, context);
8787

88-
if (response) {
88+
if(response) {
8989
console.log(`Update sent successfully. Server version: ${response.version_vector}`);
9090

9191
// Update last synced time
9292
this.lastSyncedAt = String(Date.now());
9393
this.saveLastSyncedAt();
9494

95-
if (response.version_vector === this.versionVector) {
95+
if(response.version_vector === this.versionVector) {
9696
// Our update was the latest
9797
this.hasUnsyncedChanges = false;
9898
this.saveUnsyncedFlag();
@@ -106,7 +106,7 @@ export class SyncManager {
106106
} else {
107107
return Promise.reject(response);
108108
}
109-
} catch (error) {
109+
} catch(error) {
110110
console.error('Failed to send update:', error);
111111
// Keep the unsynced flag as true
112112
this.hasUnsyncedChanges = true;
@@ -116,23 +116,23 @@ export class SyncManager {
116116
}
117117
}
118118

119-
public initialize () {
120-
if (this.hasUnsyncedChanges) {
119+
public initialize() {
120+
if(this.hasUnsyncedChanges) {
121121
console.log('Unsynced changes found. Sending update...');
122122
// Send an update if there are unsynced changes
123123
this.debouncedSendUpdate();
124124
}
125125
}
126126

127-
public getUnsyncedStatus (): boolean {
127+
public getUnsyncedStatus(): boolean {
128128
return this.hasUnsyncedChanges;
129129
}
130130

131-
public getLastSyncedAt (): string {
131+
public getLastSyncedAt(): string {
132132
return this.lastSyncedAt;
133133
}
134134

135-
public getCurrentVersionVector (): number {
135+
public getCurrentVersionVector(): number {
136136
return this.versionVector;
137137
}
138138
}

src/application/services/services.type.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@ import {
3535
TemplateCreator, TemplateCreatorFormValues, TemplateSummary,
3636
UploadTemplatePayload,
3737
} from '@/application/template.type';
38+
import { AxiosInstance } from 'axios';
3839

3940
export type AFService = PublishService & AppService & WorkspaceService & TemplateService & QuickNoteService & {
4041
getClientId: () => string;
42+
getAxiosInstance: () => AxiosInstance | null;
4143
};
4244

4345
export interface AFServiceConfig {
@@ -163,7 +165,7 @@ export interface PublishService {
163165
updatePublishHomepage: (workspaceId: string, viewId: string) => Promise<void>;
164166
removePublishHomepage: (workspaceId: string) => Promise<void>;
165167

166-
getPublishOutline (namespace: string): Promise<View[]>;
168+
getPublishOutline(namespace: string): Promise<View[]>;
167169

168170
getPublishViewGlobalComments: (viewId: string) => Promise<GlobalComment[]>;
169171
createCommentOnPublishView: (viewId: string, content: string, replyCommentId?: string) => Promise<void>;

src/application/slate-yjs/plugins/withYjs.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -102,23 +102,23 @@ export function withYjs<T extends Editor>(
102102
const initializeDocumentContent = () => {
103103
const content = yDocToSlateContent(doc);
104104

105-
if (!content) {
105+
if(!content) {
106106
return;
107107
}
108108

109109
const selection = e.selection;
110110

111-
if (readSummary) {
111+
if(readSummary) {
112112
e.children = content.children.slice(0, 10);
113113
} else {
114114
e.children = content.children;
115115
}
116116

117-
if (selection && !ReactEditor.hasRange(editor, selection)) {
117+
if(selection && !ReactEditor.hasRange(editor, selection)) {
118118
try {
119119
Transforms.select(e, Editor.start(editor, [0]));
120120

121-
} catch (e) {
121+
} catch(e) {
122122
console.error(e);
123123
editor.deselect();
124124
}
@@ -130,7 +130,7 @@ export function withYjs<T extends Editor>(
130130
};
131131

132132
const applyIntercept = (op: Operation) => {
133-
if (YjsEditor.connected(e) && !e.interceptLocalChange) {
133+
if(YjsEditor.connected(e) && !e.interceptLocalChange) {
134134
YjsEditor.storeLocalChange(e, op);
135135
}
136136

@@ -145,7 +145,7 @@ export function withYjs<T extends Editor>(
145145
e.interceptLocalChange = true;
146146

147147
// Initialize or update the document content to ensure it is in the correct state before applying remote events
148-
if (transaction.origin === CollabOrigin.Remote) {
148+
if(transaction.origin === CollabOrigin.Remote) {
149149

150150
initializeDocumentContent();
151151
} else {
@@ -154,8 +154,8 @@ export function withYjs<T extends Editor>(
154154
Editor.withoutNormalizing(e, () => {
155155
translateYEvents(e, events);
156156
});
157-
if (selection) {
158-
if (!ReactEditor.hasRange(editor, selection)) {
157+
if(selection) {
158+
if(!ReactEditor.hasRange(editor, selection)) {
159159
editor.deselect();
160160
} else {
161161
e.select(selection);
@@ -169,13 +169,13 @@ export function withYjs<T extends Editor>(
169169
};
170170

171171
const handleYEvents = (events: Array<YEvent>, transaction: Transaction) => {
172-
if (transaction.origin === CollabOrigin.Local) return;
172+
if(transaction.origin === CollabOrigin.Local) return;
173173
YjsEditor.applyRemoteEvents(e, events, transaction);
174174

175175
};
176176

177177
e.connect = () => {
178-
if (YjsEditor.connected(e)) {
178+
if(YjsEditor.connected(e)) {
179179
throw new Error('Already connected');
180180
}
181181

@@ -186,7 +186,7 @@ export function withYjs<T extends Editor>(
186186
};
187187

188188
e.disconnect = () => {
189-
if (!YjsEditor.connected(e)) {
189+
if(!YjsEditor.connected(e)) {
190190
throw new Error('Not connected');
191191
}
192192

@@ -215,7 +215,8 @@ export function withYjs<T extends Editor>(
215215
e.apply = applyIntercept;
216216

217217
e.onChange = () => {
218-
if (YjsEditor.connected(e)) {
218+
console.log('===onChange', e.operations);
219+
if(YjsEditor.connected(e)) {
219220
YjsEditor.flushLocalChanges(e);
220221
}
221222

0 commit comments

Comments
 (0)