This repository has been archived by the owner on Apr 17, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1072 from Arenukvern/feat/vue3-hooks
- Loading branch information
Showing
46 changed files
with
1,604 additions
and
190 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
# vue-datastore | ||
|
||
## Project setup | ||
|
||
``` | ||
yarn install | ||
``` | ||
|
||
### Compiles and hot-reloads for development | ||
|
||
``` | ||
yarn serve | ||
``` | ||
|
||
### Compiles and minifies for production | ||
|
||
``` | ||
yarn build | ||
``` | ||
|
||
### Lints and fixes files | ||
|
||
``` | ||
yarn lint | ||
``` | ||
|
||
### Customize configuration | ||
|
||
See [Configuration Reference](https://cli.vuejs.org/config/). | ||
|
||
# Offix - Vue3 Todo Example App | ||
|
||
This example demonstrates how to get started using Offix in a Vue project. The app is a simple | ||
todo app making use of the `offix-client` and can be used as launch pad to getting started | ||
with Offix and make use of the features in the library. | ||
|
||
## Getting started | ||
|
||
To get started, run: | ||
|
||
``` | ||
yarn install | ||
``` | ||
|
||
### Setting up a server | ||
|
||
For simplicity, a GraphQL Serve in-memory server has been provided. You can make changes to the GrapQL schema, by editing the `models/runtime.graphql` file. To start the server, run the following | ||
command: | ||
|
||
``` | ||
yarn startServer | ||
``` | ||
|
||
Alternatively, you can implement your own backend server. | ||
|
||
### Starting the client | ||
|
||
Next, configure the GraphQL server address in the `src/clientConfig.js` file: | ||
|
||
``` | ||
... | ||
const wsLink = new WebSocketLink({ | ||
uri: 'ws://<YOUR-SERVER-ADDRESS-HERE>', | ||
... | ||
}); | ||
const httpLink = new HttpLink({ | ||
uri: 'http://<YOUR-SERVER-ADDRESS-HERE>', | ||
}); | ||
... | ||
``` | ||
|
||
### Starting the client | ||
|
||
Lastly, run the following commands from the React example folder. | ||
|
||
``` | ||
yarn start | ||
``` | ||
|
||
## Adding more models | ||
|
||
1. Edit runtime.graphql file in `src/model/runtime.graphql` | ||
2. Generate models yarn generate | ||
3. Review new models | ||
|
||
## Running as native capacitor application | ||
|
||
yarn build | ||
yarn cap add ios | ||
yarn cap copy ios | ||
yarn cap open ios | ||
|
||
yarn build | ||
yarn cap add android | ||
// Swap main activity https://github.com/capacitor-community/sqlite | ||
yarn cap copy android | ||
yarn cap open android |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<link rel="icon" href="/favicon.ico" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Vite App</title> | ||
</head> | ||
<body> | ||
<div id="app"></div> | ||
<script type="module" src="/src/main.ts"></script> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
{ | ||
"name": "vue-datastore", | ||
"version": "0.0.0", | ||
"scripts": { | ||
"dev": "vite", | ||
"build": "vite build", | ||
"serve": "vite preview", | ||
"upgrade-next": "yarn yarn-upgrade-all add vue@next ant-design-vue@next", | ||
"generate": "offix generate --schema ./src/model/runtime.graphql --outputPath ./src/datastore/generated", | ||
"startServer": "gqlserve serve --datasync --conflict=clientSideWins --port=5400 ./src/model/runtime.graphql", | ||
"linkdatastore": "cd ../../packages/datastore/datastore && yarn link && cd - && yarn link offix-datastore && rm -Rf ./node_modules/react && && rm -Rf ./node_modules/react-dom", | ||
"linkdatastorecli": "cd ../../packages/datastore/cli && yarn link && cd - && yarn link @offix/cli" | ||
}, | ||
"browserslist": { | ||
"production": [ | ||
">0.2%", | ||
"not dead", | ||
"not op_mini all" | ||
], | ||
"development": [ | ||
"last 1 chrome version", | ||
"last 1 firefox version", | ||
"last 1 safari version" | ||
] | ||
}, | ||
"dependencies": { | ||
"@ant-design/colors": "^6.0.0", | ||
"ant-design-vue": "^2.0.1", | ||
"offix-datastore": "^0.4.0", | ||
"vue": "^3.0.6", | ||
"@ant-design/icons-vue": "^6.0.1" | ||
}, | ||
"devDependencies": { | ||
"@offix/cli": "^0.3.3", | ||
"@types/node": "^14.14.31", | ||
"@vitejs/plugin-vue": "^1.1.4", | ||
"@vitejs/plugin-vue-jsx": "^1.1.2", | ||
"@vue/compiler-sfc": "^3.0.5", | ||
"babel-plugin-import": "^1.13.3", | ||
"eslint": "^7.21.0", | ||
"eslint-plugin-vue": "^7.6.0", | ||
"graphback-cli": "^1.1.2", | ||
"graphql-serve": "1.1.2", | ||
"sass": "^1.32.8", | ||
"typescript": "^4.2.2", | ||
"vite": "^2.0.4", | ||
"yarn-upgrade-all": "^0.5.4" | ||
} | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
<template> | ||
<Loading v-if="loading" /> | ||
<Error v-else-if="error" :message="error.message" /> | ||
<div :style="containerStyle" v-else> | ||
<div style="width: 60%"> | ||
<a-page-header | ||
:title="isNotToAddView ? 'Offix Todo' : 'Add Todo'" | ||
@back="isNotToAddView ? null : (isToAddView = false)" | ||
> | ||
<template v-slot:extra v-if="isNotToAddView"> | ||
<a-button type="primary" @click="isToAddView = true" ghost> | ||
Add Todo | ||
</a-button> | ||
<a-button type="primary" danger ghost> | ||
{{ replicating ? "Online" : "Offline" }} | ||
</a-button> | ||
</template> | ||
<TodoList v-if="isNotToAddView" :todos="data" /> | ||
<AddTodo v-else :cancel="cancelAddTodo" /> | ||
</a-page-header> | ||
</div> | ||
</div> | ||
</template> | ||
<script lang="ts"> | ||
import { NetworkStatusEvent } from "offix-datastore/types/replication/network/NetworkStatus"; | ||
import { computed, defineComponent, onMounted, ref, toRefs, watch } from "vue"; | ||
import { Error, TodoList } from "./components"; | ||
import { datastore } from "./datastore/config"; | ||
import { useFindTodos } from "./datastore/hooks"; | ||
import Loading from "./components/UI/Loading.vue"; | ||
import AddTodo from "./components/forms/AddTodo.vue"; | ||
export default defineComponent({ | ||
name: "App", | ||
components: { | ||
AddTodo, | ||
Error, | ||
Loading, | ||
TodoList, | ||
}, | ||
setup() { | ||
const containerStyle = { | ||
display: "flex", | ||
alignItems: "start", | ||
justifyContent: "center", | ||
minHeight: "100vh", | ||
width: "100vw", | ||
padding: "2em 0", | ||
}; | ||
const replicating = ref(false); | ||
const isToAddView = ref(false); | ||
const isNotToAddView = computed(() => !isToAddView.value); | ||
const { state, subscribeToUpdates } = useFindTodos(); | ||
const { loading, data, error } = toRefs(state.value); | ||
console.log({ loading, data, error, state }); | ||
const startReplication = () => { | ||
datastore.startReplication(); | ||
replicating.value = true; | ||
}; | ||
onMounted(() => { | ||
startReplication(); | ||
datastore.getNetworkIndicator()?.subscribe({ | ||
next: (event: NetworkStatusEvent) => { | ||
if (event.isOnline) { | ||
startReplication(); | ||
} else { | ||
datastore.stopReplication(); | ||
replicating.value = false; | ||
} | ||
}, | ||
}); | ||
}); | ||
watch( | ||
data, | ||
() => { | ||
const subscription = subscribeToUpdates(); | ||
return () => subscription.unsubscribe(); | ||
}, | ||
{ deep: true, immediate: true } | ||
); | ||
const cancelAddTodo = () => (isToAddView.value = false); | ||
return { | ||
containerStyle, | ||
isToAddView, | ||
isNotToAddView, | ||
loading, | ||
replicating, | ||
error, | ||
data, | ||
cancelAddTodo, | ||
}; | ||
}, | ||
}); | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { DeleteOutlined, EditOutlined } from "@ant-design/icons-vue"; | ||
import { defineComponent, h, ref } from "@vue/runtime-core"; | ||
import { PropType } from "vue"; | ||
import { useDeleteTodo } from "../../datastore/hooks"; | ||
import EditTodo from "../forms/EditTodo.vue"; | ||
import ToggleTodo from "../forms/ToggleTodo.vue"; | ||
import { Todo } from "/@/datastore/generated"; | ||
export const TodoItem = defineComponent({ | ||
props: { | ||
todo: { | ||
type: Object as PropType<Todo>, | ||
required: true, | ||
}, | ||
}, | ||
components: { | ||
ToggleTodo, | ||
EditTodo, | ||
}, | ||
setup(props) { | ||
const { remove: deleteTodo } = useDeleteTodo(); | ||
const edit = ref(false); | ||
const handleDelete = () => { | ||
deleteTodo({ _id: props.todo._id }) | ||
.then((res) => console.log("response", res)) | ||
.catch((error: any) => console.log(error)); | ||
}; | ||
|
||
return () => | ||
h( | ||
edit.value ? ( | ||
<edit-todo | ||
todo={props.todo} | ||
toggleEdit={() => (edit.value = !edit.value)} | ||
/> | ||
) : ( | ||
<a-row justify="space-between"> | ||
<a-col> | ||
<toggle-todo todo={props.todo} /> | ||
<p> | ||
<b>Description: </b> | ||
<br /> | ||
{props.todo.description} | ||
</p> | ||
</a-col> | ||
<a-col> | ||
<a-button type="primary" onClick={() => (edit.value = true)}> | ||
<EditOutlined /> | ||
</a-button> | ||
<a-button type="primary" onClick={handleDelete} danger> | ||
<DeleteOutlined /> | ||
</a-button> | ||
</a-col> | ||
</a-row> | ||
) | ||
); | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { computed, defineComponent, h, PropType } from "vue"; | ||
import { Todo } from "../../datastore/generated"; | ||
import { Empty } from "../UI"; | ||
import { TodoItem } from "./TodoItem"; | ||
|
||
export const TodoList = defineComponent({ | ||
props: { | ||
todos: { | ||
type: Array as PropType<Todo[]>, | ||
required: true, | ||
default: () => [], | ||
}, | ||
}, | ||
components: { | ||
TodoItem, | ||
Empty, | ||
}, | ||
setup(props) { | ||
const noTodos = computed(() => !props.todos || props.todos.length === 0); | ||
return () => | ||
h("div", {}, [ | ||
noTodos.value ? h(<empty />) : null, | ||
...props.todos.map((todo) => | ||
h( | ||
<a-card key={todo._id} style='margin: "0.5em 0"'> | ||
<todo-item todo={todo} /> | ||
</a-card> | ||
) | ||
), | ||
]); | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./TodoItem"; | ||
export * from "./TodoList"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
//@ts-nocheck //FIXME: remove ignore | ||
import { defineComponent, h } from "@vue/runtime-core"; | ||
|
||
export const Empty = defineComponent({ | ||
setup() { | ||
return () => | ||
h( | ||
<div class="empty" style={{ background: "#fff" }}> | ||
<div class="empty-icon"> | ||
<i class="icon icon-3x icon-flag" /> | ||
</div> | ||
<p class="empty-title h5">You have no todo items</p> | ||
<p class="empty-subtitle">Click the button to create a new task</p> | ||
</div> | ||
); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { defineComponent, h } from "vue"; | ||
|
||
export const Error = defineComponent({ | ||
name: "Error", | ||
props: { | ||
message: { | ||
type: String, | ||
required: false, | ||
default: "", | ||
}, | ||
}, | ||
setup(props) { | ||
return () => | ||
h( | ||
<a-result | ||
status="error" | ||
title="Oops, it looks like there was an error!" | ||
subTitle={props.message} | ||
/> | ||
); | ||
}, | ||
}); |
Oops, something went wrong.