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

feat: datastore vue3 hooks and example #1072

Merged
merged 18 commits into from
Mar 19, 2021
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions examples/vue-datastore/README.md
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
13 changes: 13 additions & 0 deletions examples/vue-datastore/index.html
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>
49 changes: 49 additions & 0 deletions examples/vue-datastore/package.json
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 added examples/vue-datastore/public/favicon.ico
Binary file not shown.
93 changes: 93 additions & 0 deletions examples/vue-datastore/src/App.vue
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>
57 changes: 57 additions & 0 deletions examples/vue-datastore/src/components/Todo/TodoItem.tsx
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>
)
);
},
});
32 changes: 32 additions & 0 deletions examples/vue-datastore/src/components/Todo/TodoList.tsx
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>
)
),
]);
},
});
2 changes: 2 additions & 0 deletions examples/vue-datastore/src/components/Todo/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./TodoItem";
export * from "./TodoList";
17 changes: 17 additions & 0 deletions examples/vue-datastore/src/components/UI/Empty.tsx
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>
);
}
});
22 changes: 22 additions & 0 deletions examples/vue-datastore/src/components/UI/Error.tsx
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}
/>
);
},
});
Loading