Skip to content
This repository has been archived by the owner on Jan 13, 2023. It is now read-only.

Commit

Permalink
feat: model now updates alll states successfully!
Browse files Browse the repository at this point in the history
ready to release
  • Loading branch information
Arenukvern committed Mar 13, 2021
1 parent 59a3df8 commit 7976391
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 82 deletions.
45 changes: 20 additions & 25 deletions examples/vue-client/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Hello Vue 3 + TypeScript + Vite" />
<button @click="addTodo">Add</button>
<button @click="addTodo">Add to original state</button>
<button @click="addTodoModel">Add via standalone model</button>
<div>
<span>List Original</span>
<div v-for="todo in todoList" :key="todo.id">
<div>
<p>todo.title</p>
<p>{{ todo.title }}</p>
<p><button @click="removeTodo(todo)">x</button></p>
</div>
</div>
</div>
<div>
<span>List Duplicates</span>
<div v-for="todo in todoDuplicateList" :key="todo.id">
<div>todo.title</div>
<div>{{ todo.title }}</div>
<p><button @click="removeTodo(todo)">x</button></p>
</div>
</div>
</template>
Expand All @@ -37,45 +39,38 @@
onMounted(async () => {
const isReplicating = await todoModel.startReplication()
console.log({ isReplicating })
const todos: CreateTodoInput[] = [
{
_version: 1,
_lastUpdatedAt: Date.now().toString(),
title: 'Hello World!',
},
{
_version: 1,
_lastUpdatedAt: Date.now().toString(),
title: 'Hello Mars!',
},
]
for (const todo of todos) {
await todoModel.add({
input: todo,
})
}
todoState.find()
})
const addTodo = async () => {
const addTodo = async (arg?: { standalone?: boolean }) => {
const todo: CreateTodoInput = {
_version: 1,
_lastUpdatedAt: Date.now().toString(),
title: `New todo ${counter}`,
title: `New todo ${counter.value}`,
}
counter.value++
await todoState.add({
input: todo,
})
if (arg?.standalone) {
await todoModel.add({
input: todo,
})
} else {
await todoState.add({
input: todo,
})
}
}
const addTodoModel = () => addTodo({ standalone: true })
const removeTodo = async (todo: Todo) => {
await todoState.remove({
input: {
id: todo.id,
},
})
}
return {
addTodo,
addTodoModel,
removeTodo,
todoList: todoState.list,
todoDuplicateList: todoDubplicateState.list,
Expand Down
12 changes: 6 additions & 6 deletions examples/vue-client/src/sync/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const todoModel = CactusSync.attachModel(
QueryGetTodoArgs,
{ getTodo: Maybe<Todo> },
QueryFindTodosArgs,
TodoResultList
{ findTodos: TodoResultList }
>({ graphqlModelType: schema.getType('Todo') as Maybe<GraphQLObjectType> })
)
export const useTodoState = () => new VueStateModel({ cactusModel: todoModel })
Expand All @@ -63,7 +63,7 @@ export type TodoState = VueStateModel<
QueryGetTodoArgs,
{ getTodo: Maybe<Todo> },
QueryFindTodosArgs,
TodoResultList
{ findTodos: TodoResultList }
>
export const userModel = CactusSync.attachModel(
CactusModel.init<
Expand All @@ -77,7 +77,7 @@ export const userModel = CactusSync.attachModel(
QueryGetUserArgs,
{ getUser: Maybe<User> },
QueryFindUsersArgs,
UserResultList
{ findUsers: UserResultList }
>({ graphqlModelType: schema.getType('User') as Maybe<GraphQLObjectType> })
)
export const useUserState = () => new VueStateModel({ cactusModel: userModel })
Expand All @@ -92,7 +92,7 @@ export type UserState = VueStateModel<
QueryGetUserArgs,
{ getUser: Maybe<User> },
QueryFindUsersArgs,
UserResultList
{ findUsers: UserResultList }
>
export const cactusSyncTimestampModel = CactusSync.attachModel(
CactusModel.init<
Expand All @@ -106,7 +106,7 @@ export const cactusSyncTimestampModel = CactusSync.attachModel(
QueryGetCactusSyncTimestampArgs,
{ getCactusSyncTimestamp: Maybe<CactusSyncTimestamp> },
QueryFindCactusSyncTimestampsArgs,
CactusSyncTimestampResultList
{ findCactusSyncTimestamps: CactusSyncTimestampResultList }
>({
graphqlModelType: schema.getType(
'CactusSyncTimestamp'
Expand All @@ -126,7 +126,7 @@ export type CactusSyncTimestampState = VueStateModel<
QueryGetCactusSyncTimestampArgs,
{ getCactusSyncTimestamp: Maybe<CactusSyncTimestamp> },
QueryFindCactusSyncTimestampsArgs,
CactusSyncTimestampResultList
{ findCactusSyncTimestamps: CactusSyncTimestampResultList }
>

console.log('Cactus Sync hooks initialized')
81 changes: 76 additions & 5 deletions packages/cactus-sync-client/lib/src/abstract/CactusModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ import {
} from './ApolloRunner'
import { Maybe } from './BasicTypes'
import { CactusSync } from './CactusSync'
import {
notifyStateModelListeners,
validateStateModelResult,
} from './StateModel'

interface CactusModelInitI {
graphqlModelType: Maybe<GraphQLObjectType>
Expand Down Expand Up @@ -80,7 +84,8 @@ type OperationInput<TInput> = {

export type OperationFunction<TInput, TResult> = (
input: TInput,
gql?: Maybe<OperationFunctionGql>
gql?: Maybe<OperationFunctionGql>,
notifyListeners?: Maybe<boolean>
) => Promise<ExecutionResult<TResult>>

export type QueryOperationFunction<
Expand Down Expand Up @@ -221,8 +226,9 @@ export class CactusModel<
protected async _executeMiddleware<TInput, TResult>(arg: {
operationInput: OperationInput<TInput>
operationType: DefaultGqlOperationType
notifyListeners?: Maybe<boolean>
}) {
const { operationType, operationInput } = arg
const { operationType, operationInput, notifyListeners } = arg
const { input: variableValues, gql } = operationInput
/**
* If we receive fragmentGql, we concat it with default query
Expand All @@ -240,39 +246,104 @@ export class CactusModel<
query,
operationType,
})

/// STATE UDPATES

if (!notifyListeners) return result
const validateAndEmit = ({ remove }: { remove?: Maybe<boolean> }) => {
const { isNotValid, data } = validateStateModelResult(result)
console.log({ isNotValid, data })
if (isNotValid || data == null) return
for (const maybeModel of Object.values(data)) {
notifyStateModelListeners({
emitter: this.db.graphqlRunner.subscriptionsEmitter,
remove: remove,
item: maybeModel,
notifyListeners,
modelName: this.modelName,
})
}
}
switch (operationType) {
case DefaultGqlOperationType.create:
case DefaultGqlOperationType.update:
validateAndEmit({})
break
case DefaultGqlOperationType.remove:
validateAndEmit({ remove: true })
break
}
return result
}
add: OperationFunction<TCreateInput, TCreateResult> = async (input, gql) => {
/**
* By default this function will notify all states for this model
* You can turn off it by turning notifyListeners = false
*
* @param input
* @param gql
* @param notifyListeners
* @returns
*/
add: OperationFunction<TCreateInput, TCreateResult> = async (
input,
gql,
notifyListeners
) => {
return await this._executeMiddleware({
operationInput: {
gql,
input,
},
operationType: DefaultGqlOperationType.create,
notifyListeners: notifyListeners == false ? false : true,
})
}
/**
* By default this function will notify all states for this model
* You can turn off it by turning notifyListeners = false
*
* @param input
* @param gql
* @param notifyListeners
* @returns
*/

update: OperationFunction<TUpdateInput, TUpdateResult> = async (
input,
gql
gql,
notifyListeners
) => {
return await this._executeMiddleware({
operationInput: {
gql,
input,
},
operationType: DefaultGqlOperationType.update,
notifyListeners: notifyListeners == false ? false : true,
})
}
/**
* By default this function will notify all states for this model
* You can turn off it by turning notifyListeners = false
*
* @param input
* @param gql
* @param notifyListeners
* @returns
*/

remove: OperationFunction<TDeleteInput, TDeleteResult> = async (
input,
gql
gql,
notifyListeners
) => {
return await this._executeMiddleware({
operationInput: {
gql,
input,
},
operationType: DefaultGqlOperationType.remove,
notifyListeners: notifyListeners == false ? false : true,
})
}
get: OperationFunction<TGetInput, TGetResult> = async (input, gql) => {
Expand Down
7 changes: 3 additions & 4 deletions packages/cactus-sync-client/lib/src/abstract/CactusSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,9 @@ export class CactusSync<TCacheShape = any> extends Dexie {
modelName,
queries,
}: ModelReplicationI): Promise<boolean> {
console.log({ modelName, queries })
const isReplicating = this.isModelReplicating({ modelName })
if (isReplicating) {
this.graphqlRunner?.subscribe({ modelName, queries })
if (!isReplicating) {
this.graphqlRunner.subscribe({ modelName, queries })
this.replicatingModels.add(modelName)
}
return this.isModelReplicating({ modelName })
Expand All @@ -170,7 +169,7 @@ export class CactusSync<TCacheShape = any> extends Dexie {
}: ModelReplicationI): Promise<boolean> {
const isReplicating = this.isModelReplicating({ modelName })
if (isReplicating) {
this.graphqlRunner?.unsubscribe({ modelName })
this.graphqlRunner.unsubscribe({ modelName })
this.replicatingModels.delete(modelName)
}
return this.isModelReplicating({ modelName })
Expand Down
45 changes: 45 additions & 0 deletions packages/cactus-sync-client/lib/src/abstract/StateModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ApolloQueryResult, FetchResult } from '@apollo/client/core'
import { Emitter } from 'mitt'
import { Maybe } from './BasicTypes'

export enum StateModelEvents {
addUpdateStateModel = 'addUpdateStateModel',
removeStateModel = 'removeStateModel',
}

export type StateModelChange<TModel> = {
modelName: string
item: TModel
}
export const validateStateModelResult = <TResult>(
result: FetchResult<TResult> | ApolloQueryResult<TResult>
): { isNotValid: boolean; data?: Maybe<TResult>; isValid: boolean } => {
if (result.errors != null) return { isNotValid: true, isValid: false }
const data = result.data
if (typeof data != 'object') return { isNotValid: true, isValid: false }
return { isNotValid: false, data, isValid: true }
}
export const notifyStateModelListeners = <TModel>({
remove,
emitter,
item,
modelName,
notifyListeners,
}: {
modelName: string
notifyListeners: Maybe<boolean>
item: TModel
remove?: Maybe<boolean>
emitter: Emitter
}) => {
if (notifyListeners) {
const obj: StateModelChange<TModel> = {
modelName: modelName,
item,
}
const eventType = remove
? StateModelEvents.removeStateModel
: StateModelEvents.addUpdateStateModel
emitter.emit(eventType, obj)
}
}
Loading

0 comments on commit 7976391

Please sign in to comment.