5 Commits

Author SHA1 Message Date
Jonathon Broughton dc253e423a Update Timeago dependency
Specifically downrevving to match Vue2
2023-02-14 19:42:44 +00:00
Alan Rynne bf6d2bb681 Merge pull request #3 from specklesystems/alan/cleanup
Cleanup: Removed promises in favour of async/await
2021-05-18 13:36:43 +02:00
Alan Rynne 95c4410bf2 cleanup: Added minor comments in speckleUtils.js 2021-05-18 13:23:24 +02:00
Alan Rynne a8a9145846 cleanup: Removed all promises in favour of async/await 2021-05-18 13:03:31 +02:00
Alan Rynne 270fc5bd5a feat: Added gif to readme 2021-04-30 14:28:08 +02:00
10 changed files with 8071 additions and 5093 deletions
+6 -2
View File
@@ -4,14 +4,18 @@
This repo contains all the code related to the [Create your own App guide](https://speckle.guide/dev/apps.html), which is part of our [Developer Docs](https://speckle.guide/dev)
> You can find the published app [HERE](https://hardcore-einstein-829a53.netlify.app/)
>
> You can find the published app [HERE](https://hardcore-einstein-829a53.netlify.app/)
>
> [![Netlify Status](https://api.netlify.com/api/v1/badges/45628834-7477-42f8-9e2a-e8da5b4d2de1/deploy-status)](https://app.netlify.com/sites/hardcore-einstein-829a53/deploys)
Currently, only _Part I_ of the guide has been released. You can find a `tag` with the name `part-i` in the repo, which points to the final code for the corresponding parts. Future releases will follow the same pattern.
For instructions on how to install dependencies and basic setup, please consult the guide.
## Preview
![Full demo](./app-guide-full-demo.gif)
## Environment variables
You'll find an `.env.local` file already populated with some values. **Please remember to modify these values with your own, following the guide linked above.**
Binary file not shown.

After

Width:  |  Height:  |  Size: 834 KiB

+7987 -5022
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -15,7 +15,7 @@
"register-service-worker": "^1.7.1",
"vue": "^2.6.11",
"vue-router": "^3.2.0",
"vue-timeago": "^5.1.3",
"vue2-timeago": "^2.0.9",
"vuetify": "^2.4.11",
"vuex": "^3.4.0",
"vuex-persist": "^3.1.3"
+3 -5
View File
@@ -57,12 +57,10 @@ export default {
}
},
methods: {
fetchSearchResults(e) {
async fetchSearchResults(e) {
if (!e || e?.length < 3) return
return searchStreams(e)
.then(json => {
this.streams = json.data.streams
})
var json = await searchStreams(e)
this.streams = json.data.streams
},
debounceInput: debounce(function (e) {
this.fetchSearchResults(e)
+1 -1
View File
@@ -5,7 +5,7 @@ import router from './router'
import store from './store'
import vuetify from './plugins/vuetify'
import VueTimeago from 'vue-timeago'
import VueTimeago from 'vue2-timeago'
Vue.use(VueTimeago, { locale: 'en' })
Vue.config.productionTip = false
+15 -11
View File
@@ -21,19 +21,23 @@ const router = new VueRouter({
router.beforeEach( async (to, from, next) => {
if(to.query.access_code){
// If the route contains an access code, exchange it and go home.
store.dispatch('exchangeAccessCode', to.query.access_code)
.then(() => next("/"))
.catch(err => {
console.warn("exchange failed", err);
next("/")
})
// If the route contains an access code, exchange it
try {
await store.dispatch('exchangeAccessCode', to.query.access_code)
} catch (err){
console.warn("exchange failed", err);
}
// Whatever happens, go home.
next("/")
}
else {
// Check on every route change if you still have access.
store.dispatch("getUser")
.then(to => next(to))
.catch(err => next("/"))
try {
// Check on every route change if you still have access.
var goto = await store.dispatch("getUser")
next(goto)
} catch (err) {
next("/")
}
}
})
+36 -23
View File
@@ -6,6 +6,7 @@ export const TOKEN = `${APP_NAME}.AuthToken`
export const REFRESH_TOKEN = `${APP_NAME}.RefreshToken`
export const CHALLENGE = `${APP_NAME}.Challenge`
// Redirects to the Speckle server authentication page, using a randomly generated challenge. Challenge will be stored to compare with when exchanging the access code.
export function goToSpeckleAuthPage() {
// Generate random challenge
var challenge = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
@@ -15,13 +16,16 @@ export function goToSpeckleAuthPage() {
window.location = `${SERVER_URL}/authn/verify/${process.env.VUE_APP_SPECKLE_ID}/${challenge}`
}
export function speckleLogOut(){
// Log out the current user. This removes the token/refreshToken pair.
export function speckleLogOut() {
// Remove both token and refreshToken from localStorage
localStorage.removeItem(TOKEN)
localStorage.removeItem(REFRESH_TOKEN)
}
export function exchangeAccessCode(accessCode){
return fetch(`${SERVER_URL}/auth/token/`, {
// Exchanges the provided access code with a token/refreshToken pair, and saves them to local storage.
export async function exchangeAccessCode(accessCode) {
var res = await fetch(`${SERVER_URL}/auth/token/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -32,38 +36,47 @@ export function exchangeAccessCode(accessCode){
appSecret: process.env.VUE_APP_SPECKLE_SECRET,
challenge: localStorage.getItem(CHALLENGE)
})
}).then(res => res.json()).then(data => {
if (data.token) {
localStorage.removeItem(CHALLENGE)
localStorage.setItem(TOKEN, data.token)
localStorage.setItem(REFRESH_TOKEN, data.refreshToken)
}
return data
})
var data = await res.json()
if (data.token) {
// If retrieving the token was successful, remove challenge and set the new token and refresh token
localStorage.removeItem(CHALLENGE)
localStorage.setItem(TOKEN, data.token)
localStorage.setItem(REFRESH_TOKEN, data.refreshToken)
}
return data
}
export function speckleFetch(query){
// Calls the GraphQL endpoint of the Speckle server with a specific query.
export async function speckleFetch(query) {
let token = localStorage.getItem(TOKEN)
if (token)
return fetch(
`${SERVER_URL}/graphql`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: query
try {
var res = await fetch(
`${SERVER_URL}/graphql`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: query
})
})
})
.then(res => res.json())
return await res.json()
} catch (err) {
console.error("API call failed", err)
}
else
return Promise.reject("You are not logged in (token does not exist)")
}
// Fetch the current user data using the userInfoQuery
export const getUserData = () => speckleFetch(userInfoQuery())
// Fetch for streams matching the specified text using the streamSearchQuery
export const searchStreams = (e) => speckleFetch(streamSearchQuery(e))
// Get commits related to a specific stream, allows for pagination by passing a cursor
export const getStreamCommits = (streamId, itemsPerPage, cursor) => speckleFetch(streamCommitsQuery(streamId, itemsPerPage, cursor))
+15 -20
View File
@@ -70,34 +70,29 @@ export default new Vuex.Store({
// Here, we could save the tokens to the store if necessary.
return exchangeAccessCode(accessCode)
},
getUser(context) {
return getUserData()
.then(json => {
var data = json.data
context.commit("setUser", data.user)
context.commit("setServerInfo", data.serverInfo)
})
.catch(err => {
console.error(err)
})
async getUser(context) {
try {
var json = await getUserData()
var data = json.data
context.commit("setUser", data.user)
context.commit("setServerInfo", data.serverInfo)
} catch (err) {
console.error(err)
}
},
redirectToAuth() {
goToSpeckleAuthPage()
},
handleStreamSelection(context, stream) {
async handleStreamSelection(context, stream) {
context.commit("setCurrentStream", stream)
context.commit("setTableOptions", { itemsPerPage: 5 })
context.commit("resetPrevCursors")
return getStreamCommits(stream.id, 5, null)
.then(json => {
context.commit("setCommits", json.data.stream.commits)
})
var json = await getStreamCommits(stream.id, 5, null)
context.commit("setCommits", json.data.stream.commits)
},
getCommits(context, cursor) {
return getStreamCommits(context.state.currentStream.id, 5, cursor)
.then(json => {
context.commit("setCommits", json.data.stream.commits)
})
async getCommits(context, cursor) {
var json = await getStreamCommits(context.state.currentStream.id, 5, cursor)
context.commit("setCommits", json.data.stream.commits)
},
clearStreamSelection(context){
context.commit("setCurrentStream", null)
+7 -8
View File
@@ -88,22 +88,21 @@ export default {
},
watch: {
options: {
handler(val, oldval) {
async handler(val, oldval) {
this.$store.commit("setTableOptions", val)
if (oldval.page && val.page != oldval.page) {
if (val.page > oldval.page) {
this.loading = true
var cursor = this.$store.state.latestCommits.cursor
this.$store.dispatch("getCommits", cursor).then(() => {
this.$store.commit("addCursorToPreviousList", cursor)
this.loading = false
})
await this.$store.dispatch("getCommits", cursor)
this.$store.commit("addCursorToPreviousList", cursor)
this.loading = false
} else {
console.log("page down")
this.loading = true
this.$store.dispatch("getCommits", this.previousCursors[val.page - 1]).then(() => {
this.loading = false
})
await this.$store.dispatch("getCommits", this.previousCursors[val.page - 1])
this.loading = false
}
}
},