Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dc253e423a | |||
| bf6d2bb681 | |||
| 95c4410bf2 | |||
| a8a9145846 | |||
| 270fc5bd5a |
@@ -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/)
|
||||
>
|
||||
> [](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
|
||||
|
||||

|
||||
|
||||
## 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 |
Generated
+7987
-5022
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -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"
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user