feat(frontend): various improvements
branches edit/delete/create; fixed and removed dupe apollo queries and minimised requests; fixed dialog operations
This commit is contained in:
@@ -3,12 +3,7 @@
|
||||
<v-app-bar app color="background2">
|
||||
<v-container class="py-0 fill-height hidden-sm-and-down">
|
||||
<v-btn text to="/" active-class="no-active">
|
||||
<v-img
|
||||
contain
|
||||
max-height="30"
|
||||
max-width="30"
|
||||
src="./assets/logo.svg"
|
||||
/>
|
||||
<v-img contain max-height="30" max-width="30" src="./assets/logo.svg" />
|
||||
<div class="mt-1">
|
||||
<span class="primary--text"><b></b></span>
|
||||
</div>
|
||||
@@ -38,12 +33,7 @@
|
||||
</v-col>
|
||||
<v-col class="text-center">
|
||||
<v-btn text to="/" active-class="no-active" icon>
|
||||
<v-img
|
||||
contain
|
||||
max-height="40"
|
||||
max-width="40"
|
||||
src="./assets/logo.svg"
|
||||
/>
|
||||
<v-img contain max-height="40" max-width="40" src="./assets/logo.svg" />
|
||||
</v-btn>
|
||||
</v-col>
|
||||
<v-col class="text-right" style="margin-top: 8px">
|
||||
@@ -52,11 +42,7 @@
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-app-bar>
|
||||
<v-card
|
||||
v-show="showMobileMenu"
|
||||
style="position: relative; top: 40px"
|
||||
class="pa-5"
|
||||
>
|
||||
<v-card v-show="showMobileMenu" style="position: relative; top: 40px" class="pa-5">
|
||||
<v-row>
|
||||
<v-col v-for="link in navLinks" :key="link.name" cols="12">
|
||||
<v-btn text block :to="link.link">
|
||||
@@ -75,22 +61,22 @@
|
||||
</v-app>
|
||||
</template>
|
||||
<script>
|
||||
import userQuery from "./graphql/user.gql"
|
||||
import gql from "graphql-tag"
|
||||
import UserMenuTop from "./components/UserMenuTop"
|
||||
import SearchBar from "./components/SearchBar"
|
||||
import userQuery from './graphql/user.gql'
|
||||
import gql from 'graphql-tag'
|
||||
import UserMenuTop from './components/UserMenuTop'
|
||||
import SearchBar from './components/SearchBar'
|
||||
|
||||
export default {
|
||||
components: { UserMenuTop, SearchBar },
|
||||
data: () => ({
|
||||
search: "",
|
||||
search: '',
|
||||
showMobileMenu: false,
|
||||
streams: { items: [] },
|
||||
selectedSearchResult: null,
|
||||
navLinks: [
|
||||
{ link: "/streams", name: "streams" },
|
||||
{ link: "/profile", name: "profile" },
|
||||
{ link: "/help", name: "help" }
|
||||
{ link: '/streams', name: 'streams' },
|
||||
{ link: '/profile', name: 'profile' },
|
||||
{ link: '/help', name: 'help' }
|
||||
]
|
||||
}),
|
||||
apollo: {
|
||||
@@ -126,7 +112,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
background() {
|
||||
let theme = this.$vuetify.theme.dark ? "dark" : "light"
|
||||
let theme = this.$vuetify.theme.dark ? 'dark' : 'light'
|
||||
return `background-color: ${this.$vuetify.theme.themes[theme].background};`
|
||||
}
|
||||
},
|
||||
@@ -159,70 +145,18 @@ export default {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.v-card__text,
|
||||
.v-card__title {
|
||||
word-break: normal !important;
|
||||
}
|
||||
|
||||
.streamid {
|
||||
font-family: monospace !important;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: rgba(10, 102, 255, 0.25);
|
||||
}
|
||||
|
||||
.no-decor a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.v-btn--active.no-active::before {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
.hoverable-border {
|
||||
border: 1px transparent;
|
||||
}
|
||||
|
||||
.hoverable-border:hover {
|
||||
border: 1px blue;
|
||||
}
|
||||
|
||||
/* .theme--dark {
|
||||
/color: #cfcdcc !important;
|
||||
} */
|
||||
|
||||
/* don't like fat text */
|
||||
.v-list-item--dense .v-list-item__title,
|
||||
.v-list-item--dense .v-list-item__subtitle,
|
||||
.v-list--dense .v-list-item .v-list-item__title,
|
||||
.v-list--dense .v-list-item .v-list-item__subtitle {
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
|
||||
/*WHYYYY*/
|
||||
.v-tooltip__content {
|
||||
pointer-events: all !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
/* DARK MODE HARD FIXES */
|
||||
|
||||
.theme--dark.v-list {
|
||||
background-color: #303132 !important;
|
||||
}
|
||||
/*.v-application code {
|
||||
background-color: #969696;
|
||||
color: #171717;
|
||||
padding: 0 0.4rem;
|
||||
}*/
|
||||
|
||||
/* TOOLTIPs */
|
||||
|
||||
.tooltip {
|
||||
display: block !important;
|
||||
z-index: 10000;
|
||||
font-family: "Roboto", sans-serif !important;
|
||||
font-family: 'Roboto', sans-serif !important;
|
||||
font-size: 0.75rem !important;
|
||||
}
|
||||
|
||||
@@ -243,11 +177,11 @@ a:hover {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tooltip[x-placement^="top"] {
|
||||
.tooltip[x-placement^='top'] {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.tooltip[x-placement^="top"] .tooltip-arrow {
|
||||
.tooltip[x-placement^='top'] .tooltip-arrow {
|
||||
border-width: 5px 5px 0 5px;
|
||||
border-left-color: transparent !important;
|
||||
border-right-color: transparent !important;
|
||||
@@ -258,11 +192,11 @@ a:hover {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.tooltip[x-placement^="bottom"] {
|
||||
.tooltip[x-placement^='bottom'] {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.tooltip[x-placement^="bottom"] .tooltip-arrow {
|
||||
.tooltip[x-placement^='bottom'] .tooltip-arrow {
|
||||
border-width: 0 5px 5px 5px;
|
||||
border-left-color: transparent !important;
|
||||
border-right-color: transparent !important;
|
||||
@@ -273,11 +207,11 @@ a:hover {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.tooltip[x-placement^="right"] {
|
||||
.tooltip[x-placement^='right'] {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.tooltip[x-placement^="right"] .tooltip-arrow {
|
||||
.tooltip[x-placement^='right'] .tooltip-arrow {
|
||||
border-width: 5px 5px 5px 0;
|
||||
border-left-color: transparent !important;
|
||||
border-top-color: transparent !important;
|
||||
@@ -288,11 +222,11 @@ a:hover {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.tooltip[x-placement^="left"] {
|
||||
.tooltip[x-placement^='left'] {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.tooltip[x-placement^="left"] .tooltip-arrow {
|
||||
.tooltip[x-placement^='left'] .tooltip-arrow {
|
||||
border-width: 5px 0 5px 5px;
|
||||
border-top-color: transparent !important;
|
||||
border-right-color: transparent !important;
|
||||
@@ -315,13 +249,13 @@ a:hover {
|
||||
border-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.tooltip[aria-hidden="true"] {
|
||||
.tooltip[aria-hidden='true'] {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s, visibility 0.15s;
|
||||
}
|
||||
|
||||
.tooltip[aria-hidden="false"] {
|
||||
.tooltip[aria-hidden='false'] {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
transition: opacity 0.15s;
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<h3>Are you sure?</h3>
|
||||
You cannot undo this action. This will permanently delete the
|
||||
<b>{{ token.name }}</b>
|
||||
app. Any scripts relying on it will stop working.
|
||||
token. Any scripts relying on it will stop working.
|
||||
<v-divider class="my-3"></v-divider>
|
||||
<v-btn text color="error" @click="revokeToken">Delete</v-btn>
|
||||
<v-btn @click="showRevokeConfirm = false">Cancel</v-btn>
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
<template>
|
||||
<v-card rounded="lg" class="pa-4" elevation="0" color="transparent">
|
||||
<div v-if="!stream">
|
||||
<v-skeleton-loader type="card, article, article"></v-skeleton-loader>
|
||||
</div>
|
||||
<div v-if="stream">
|
||||
<v-card rounded="lg" class="pa-4" elevation="0" color="transparent" :loading="$apollo.loading">
|
||||
<template slot="progress">
|
||||
<v-progress-linear indeterminate></v-progress-linear>
|
||||
</template>
|
||||
<div>
|
||||
<v-card-title class="mr-8">
|
||||
<router-link v-show="!isHomeRoute" :to="'/streams/' + stream.id">
|
||||
<router-link
|
||||
v-show="!isHomeRoute"
|
||||
:to="'/streams/' + stream.id"
|
||||
class="text-decoration-none"
|
||||
>
|
||||
{{ stream.name }}
|
||||
</router-link>
|
||||
<div v-show="isHomeRoute">
|
||||
@@ -94,6 +98,7 @@
|
||||
</v-card>
|
||||
</template>
|
||||
<script>
|
||||
import streamQuery from '../graphql/stream.gql'
|
||||
import EditStreamDialog from '../components/dialogs/EditStreamDialog'
|
||||
import StreamShareDialog from '../components/dialogs/StreamShareDialog'
|
||||
import UserAvatar from '../components/UserAvatar'
|
||||
@@ -105,15 +110,21 @@ export default {
|
||||
UserAvatar
|
||||
},
|
||||
props: {
|
||||
stream: {
|
||||
type: Object,
|
||||
default: () => null
|
||||
},
|
||||
userRole: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
apollo: {
|
||||
stream: {
|
||||
query: streamQuery,
|
||||
variables() {
|
||||
return {
|
||||
id: this.$route.params.streamId
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data: () => ({
|
||||
editStreamDialog: false,
|
||||
dialogShare: false
|
||||
@@ -136,8 +147,9 @@ export default {
|
||||
methods: {
|
||||
editClosed() {
|
||||
this.editStreamDialog = false
|
||||
this.$emit('refresh')
|
||||
this.$apollo.queries.stream.refetch()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
||||
|
||||
@@ -2,17 +2,16 @@
|
||||
<v-card color="background2" class="elevation-0 mt-3">
|
||||
<v-card-title>Personal Access Tokens</v-card-title>
|
||||
<v-card-text>
|
||||
Personal Access Tokens can be used to access the Speckle API on this
|
||||
server; they function like ordinary OAuth access tokens. Use them in your
|
||||
scripts or apps!
|
||||
Personal Access Tokens can be used to access the Speckle API on this server; they function
|
||||
like ordinary OAuth access tokens. Use them in your scripts or apps!
|
||||
<b>
|
||||
Treat them like a password: do not post them anywhere where they could
|
||||
be accessed by others (e.g., public repos).
|
||||
Treat them like a password: do not post them anywhere where they could be accessed by others
|
||||
(e.g., public repos).
|
||||
</b>
|
||||
</v-card-text>
|
||||
<v-card-text v-if="$apollo.loading">Loading...</v-card-text>
|
||||
<v-card-text v-if="tokens && tokens.length != 0">
|
||||
<v-list three-line>
|
||||
<v-list three-line class="transparent">
|
||||
<list-item-token
|
||||
v-for="token in tokens"
|
||||
:key="token.id"
|
||||
@@ -23,7 +22,7 @@
|
||||
</v-card-text>
|
||||
<v-card-text v-else>You have no api tokens.</v-card-text>
|
||||
<v-card-text>
|
||||
<v-btn @click="tokenDialog = true" class="mb-5">new token</v-btn>
|
||||
<v-btn class="mb-5" @click="tokenDialog = true">new token</v-btn>
|
||||
<v-dialog v-model="tokenDialog" persistent width="500">
|
||||
<token-dialog @token-added="refreshList" @close="tokenDialog = false" />
|
||||
</v-dialog>
|
||||
@@ -31,9 +30,9 @@
|
||||
</v-card>
|
||||
</template>
|
||||
<script>
|
||||
import gql from "graphql-tag"
|
||||
import ListItemToken from "./ListItemPersonalAccessToken"
|
||||
import TokenDialog from "./dialogs/TokenDialog"
|
||||
import gql from 'graphql-tag'
|
||||
import ListItemToken from './ListItemPersonalAccessToken'
|
||||
import TokenDialog from './dialogs/TokenDialog'
|
||||
|
||||
export default {
|
||||
components: { ListItemToken, TokenDialog },
|
||||
|
||||
@@ -2,18 +2,13 @@
|
||||
<v-card color="background2" class="elevation-0 mt-3 mb-5">
|
||||
<v-card-title>Applications</v-card-title>
|
||||
<v-card-text>
|
||||
Register and manage third-party Speckle Apps that, once authorised by a
|
||||
user on this server, can act on their behalf.
|
||||
Register and manage third-party Speckle Apps that, once authorised by a user on this server,
|
||||
can act on their behalf.
|
||||
</v-card-text>
|
||||
<v-card-text v-if="$apollo.loading">Loading...</v-card-text>
|
||||
<v-card-text v-if="apps && apps.length !== 0">
|
||||
<v-list two-line>
|
||||
<list-item-user-app
|
||||
v-for="app in apps"
|
||||
:key="app.id"
|
||||
:app="app"
|
||||
@deleted="refreshList"
|
||||
/>
|
||||
<v-list two-line class="transparent">
|
||||
<list-item-user-app v-for="app in apps" :key="app.id" :app="app" @deleted="refreshList" />
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
<v-card-text v-else>You have no apps.</v-card-text>
|
||||
@@ -26,9 +21,9 @@
|
||||
</v-card>
|
||||
</template>
|
||||
<script>
|
||||
import gql from "graphql-tag"
|
||||
import ListItemUserApp from "./ListItemUserApp"
|
||||
import NewAppDialog from "./dialogs/NewAppDialog"
|
||||
import gql from 'graphql-tag'
|
||||
import ListItemUserApp from './ListItemUserApp'
|
||||
import NewAppDialog from './dialogs/NewAppDialog'
|
||||
|
||||
export default {
|
||||
components: { ListItemUserApp, NewAppDialog },
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
<template>
|
||||
<v-card color="transparent" class="elevation-0">
|
||||
<v-card color="transparent" class="elevation-0 text-center">
|
||||
<div v-if="!user">
|
||||
<v-skeleton-loader type="card"></v-skeleton-loader>
|
||||
</div>
|
||||
<div v-else>
|
||||
<v-card-title class="text-center mb-5 mt-5 pt-5 pb-5">
|
||||
<v-card-title class="text-center mb-5 mt-5 pt-15 pb-15">
|
||||
<v-btn
|
||||
v-tooltip="'Change your profile picture.'"
|
||||
color="transparent"
|
||||
text
|
||||
block
|
||||
:disabled="!isSelf"
|
||||
class="elevation-0 pa-0 ma-0"
|
||||
>
|
||||
<v-avatar class="elevation-1" size="124" @click="avatarDialog = true">
|
||||
<v-avatar class="elevation-0" size="100" @click="avatarDialog = true">
|
||||
<v-img v-if="user.avatar" :src="user.avatar" />
|
||||
<v-img
|
||||
v-else
|
||||
:src="`https://robohash.org/` + user.id + `.png?size=64x64`"
|
||||
/>
|
||||
<v-img v-else :src="`https://robohash.org/` + user.id + `.png?size=64x64`" />
|
||||
</v-avatar>
|
||||
</v-btn>
|
||||
<v-dialog v-model="avatarDialog" max-width="400">
|
||||
<v-card>
|
||||
<v-card-title class="text-center">
|
||||
Choose a new profile picture
|
||||
</v-card-title>
|
||||
<v-card-title class="text-center">Choose a new profile picture</v-card-title>
|
||||
<v-card-text class="text-center pa-0 ma-0 mt-5">
|
||||
<v-image-input
|
||||
v-model="imageData"
|
||||
@@ -38,7 +34,7 @@
|
||||
/>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<span class="caption" v-if="imageData">You look wonderful!</span>
|
||||
<span v-if="imageData" class="caption">You look wonderful!</span>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn text @click="avatarDialog = false">cancel</v-btn>
|
||||
<v-btn :disabled="!imageData" @click="updateAvatar">Save</v-btn>
|
||||
@@ -46,7 +42,7 @@
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-card-title>
|
||||
<v-card-title>
|
||||
<v-card-title class="text-center justify-center">
|
||||
{{ user.name }}
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
@@ -75,9 +71,9 @@
|
||||
</v-card>
|
||||
</template>
|
||||
<script>
|
||||
import gql from "graphql-tag"
|
||||
import UserDialog from "../components/dialogs/UserDialog"
|
||||
import VImageInput from "vuetify-image-input/a-la-carte"
|
||||
import gql from 'graphql-tag'
|
||||
import UserDialog from '../components/dialogs/UserDialog'
|
||||
import VImageInput from 'vuetify-image-input/a-la-carte'
|
||||
|
||||
export default {
|
||||
components: { UserDialog, VImageInput },
|
||||
@@ -97,7 +93,7 @@ export default {
|
||||
computed: {
|
||||
isSelf() {
|
||||
if (!this.user) return false
|
||||
return this.user.id === localStorage.getItem("uuid")
|
||||
return this.user.id === localStorage.getItem('uuid')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -1,186 +0,0 @@
|
||||
<template>
|
||||
<v-dialog v-model="show" width="500" @keydown.esc="cancel">
|
||||
<v-card class="pa-4" color="background2">
|
||||
<v-card-title class="subtitle-1">
|
||||
{{ isEdit ? `Edit` : `New` }} Branch
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text class="pl-2 pr-2 pt-0 pb-0">
|
||||
<v-form
|
||||
ref="form"
|
||||
v-model="valid"
|
||||
lazy-validation
|
||||
@submit.prevent="agree"
|
||||
>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="12" class="pb-0">
|
||||
<v-text-field
|
||||
v-model="branch.name"
|
||||
label="Name"
|
||||
:rules="nameRules"
|
||||
required
|
||||
filled
|
||||
:disabled="branch.name == 'main'"
|
||||
autofocus
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" class="pt-0 pb-0">
|
||||
<v-textarea
|
||||
v-model="branch.description"
|
||||
filled
|
||||
rows="2"
|
||||
label="Description"
|
||||
></v-textarea>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row v-if="isEdit && branch.name != 'main'">
|
||||
<v-col cols="12" class="pt-2 pb-2">
|
||||
<div v-if="!pendingDelete">
|
||||
<v-btn
|
||||
color="error"
|
||||
depressed
|
||||
class="mt-5"
|
||||
@click="pendingDelete = true"
|
||||
>
|
||||
Delete Branch
|
||||
</v-btn>
|
||||
<p
|
||||
class="ml-4 mt-0 pt-0 caption"
|
||||
style="display: inline-flex; width: 250px"
|
||||
>
|
||||
Delete this branch forever, no going back here!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-if="pendingDelete">
|
||||
<v-btn color="error" depressed @click.native="doDelete">
|
||||
Yes
|
||||
</v-btn>
|
||||
<v-btn class="ml-5" depressed @click="pendingDelete = false">
|
||||
No
|
||||
</v-btn>
|
||||
<p
|
||||
class="ml-4 mt-0 pt-0 caption"
|
||||
style="display: inline-flex; width: 150px"
|
||||
>
|
||||
Are you sure?
|
||||
</p>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="primary" text @click.native="agree">
|
||||
{{ isEdit ? `Save` : `Create` }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: ["branches"],
|
||||
data() {
|
||||
return {
|
||||
dialog: false,
|
||||
branch: {},
|
||||
name: "",
|
||||
nameRules: [],
|
||||
description: "",
|
||||
valid: true,
|
||||
isEdit: false,
|
||||
pendingDelete: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
show: {
|
||||
get() {
|
||||
return this.dialog
|
||||
},
|
||||
set(value) {
|
||||
this.dialog = value
|
||||
if (value === false) {
|
||||
this.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
"branch.name"(val) {
|
||||
this.nameRules = []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open(branch, streamId) {
|
||||
//set defaults
|
||||
this.dialog = true
|
||||
this.pendingDelete = false
|
||||
this.isEdit = false
|
||||
this.branch = {}
|
||||
|
||||
if (this.$refs.form) this.$refs.form.resetValidation()
|
||||
|
||||
if (branch && streamId) {
|
||||
this.branch = {
|
||||
id: branch.id,
|
||||
streamId: streamId,
|
||||
name: branch.name,
|
||||
description: branch.description
|
||||
}
|
||||
|
||||
this.isEdit = true
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.resolve = resolve
|
||||
this.reject = reject
|
||||
})
|
||||
},
|
||||
agree() {
|
||||
//prevents annoying validation message from popping at each keystroke
|
||||
//to be used in conjunction with the watch event above and the timer function
|
||||
//source: https://stackoverflow.com/a/57555332
|
||||
this.nameRules = [
|
||||
(v) => !!v || "Branches need a name too!",
|
||||
(v) =>
|
||||
(v &&
|
||||
this.branches.filter((e) => e.name === v && e.id !== this.branch.id)
|
||||
.length === 0) ||
|
||||
"A branch with this name already exists",
|
||||
(v) => (v && v.length <= 25) || "Name must be less than 25 characters",
|
||||
(v) => (v && v.length >= 3) || "Name must be at least 3 characters"
|
||||
]
|
||||
|
||||
let self = this
|
||||
setTimeout(function () {
|
||||
if (self.$refs.form.validate()) {
|
||||
self.resolve({
|
||||
result: true,
|
||||
branch: self.branch
|
||||
})
|
||||
self.dialog = false
|
||||
}
|
||||
})
|
||||
},
|
||||
cancel() {
|
||||
this.resolve({
|
||||
result: false
|
||||
})
|
||||
this.dialog = false
|
||||
},
|
||||
doDelete() {
|
||||
this.resolve({
|
||||
result: true,
|
||||
delete: true
|
||||
})
|
||||
this.dialog = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<v-card :loading="loading">
|
||||
<template slot="progress">
|
||||
<v-progress-linear indeterminate></v-progress-linear>
|
||||
</template>
|
||||
<div v-if="branch.name !== 'main'">
|
||||
<v-card-title>Edit Branch</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form ref="form" v-model="valid" lazy-validation>
|
||||
<v-text-field
|
||||
v-model="name"
|
||||
label="Name"
|
||||
:rules="nameRules"
|
||||
required
|
||||
autofocus
|
||||
></v-text-field>
|
||||
<v-textarea v-model="description" rows="2" label="Description"></v-textarea>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn color="primary" block :disabled="!valid" @click="updateBranch">Save</v-btn>
|
||||
</v-card-actions>
|
||||
<v-card-actions class="error--text body-2 pa-2">
|
||||
<v-btn block x-small text color="error" @click="showDelete = true">Delete Branch</v-btn>
|
||||
<v-dialog v-model="showDelete" max-width="500">
|
||||
<v-card>
|
||||
<v-card-title>Are you sure?</v-card-title>
|
||||
<v-card-text>
|
||||
You cannot undo this action. The branch
|
||||
<b>{{ name }}</b>
|
||||
will be permanently deleted.
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn @click="showDelete = false">Cancel</v-btn>
|
||||
<v-btn color="error" text @click="deleteBranch">Delete</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-card-actions>
|
||||
</div>
|
||||
<div v-else>
|
||||
<v-card-text>You cannot edit the main branch.</v-card-text>
|
||||
</div>
|
||||
</v-card>
|
||||
</template>
|
||||
<script>
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
streamId: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
branch: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
name: null,
|
||||
description: null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
valid: true,
|
||||
loading: false,
|
||||
name: this.branch.name,
|
||||
showDelete: false,
|
||||
nameRules: [
|
||||
(v) => !!v || 'Branches need a name too!',
|
||||
(v) =>
|
||||
(v && this.allBranchNames.findIndex((e) => e === v) === -1) ||
|
||||
'A branch with this name already exists',
|
||||
(v) => (v && v.length <= 25) || 'Name must be less than 25 characters',
|
||||
(v) => (v && v.length >= 3) || 'Name must be at least 3 characters',
|
||||
],
|
||||
description: this.branch.description,
|
||||
isEdit: false,
|
||||
pendingDelete: false,
|
||||
allBranchNames: []
|
||||
}
|
||||
},
|
||||
apollo: {
|
||||
allBranchNames: {
|
||||
query: gql`
|
||||
query branchNames($id: String!) {
|
||||
stream(id: $id) {
|
||||
id
|
||||
branches {
|
||||
items {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables() {
|
||||
return {
|
||||
id: this.$route.params.streamId
|
||||
}
|
||||
},
|
||||
update(data) {
|
||||
return data.stream.branches.items
|
||||
.map((b) => b.name)
|
||||
.filter((name) => name !== this.branch.name)
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
async deleteBranch() {
|
||||
this.loading = true
|
||||
try {
|
||||
await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation branchDelete($params: BranchDeleteInput!) {
|
||||
branchDelete(branch: $params)
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
params: {
|
||||
streamId: this.$route.params.streamId,
|
||||
id: this.branch.id
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
this.$emit('close', { deleted: true })
|
||||
this.loading = false
|
||||
},
|
||||
async updateBranch() {
|
||||
this.loading = true
|
||||
await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation branchUpdate($params: BranchUpdateInput!) {
|
||||
branchUpdate(branch: $params)
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
params: {
|
||||
streamId: this.$route.params.streamId,
|
||||
id: this.branch.id,
|
||||
name: this.name,
|
||||
description: this.description
|
||||
}
|
||||
}
|
||||
})
|
||||
this.loading = false
|
||||
this.$emit('close', { name: this.name })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<v-card :loading="loading">
|
||||
<template slot="progress">
|
||||
<v-progress-linear indeterminate></v-progress-linear>
|
||||
</template>
|
||||
<v-card-title>New Branch</v-card-title>
|
||||
<v-card-text>
|
||||
<v-form ref="form" v-model="valid" lazy-validation>
|
||||
<v-text-field
|
||||
v-model="name"
|
||||
label="Name"
|
||||
:rules="nameRules"
|
||||
required
|
||||
autofocus
|
||||
></v-text-field>
|
||||
<v-textarea v-model="description" rows="2" label="Description"></v-textarea>
|
||||
</v-form>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="primary" text :disabled="!valid" @click="createBranch">Save</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
<script>
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
streamId: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
branchNames: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
valid: true,
|
||||
loading: false,
|
||||
name: null,
|
||||
nameRules: [
|
||||
(v) => !!v || 'Branches need a name too!',
|
||||
(v) =>
|
||||
(v && this.branchNames.findIndex((e) => e === v) === -1) ||
|
||||
'A branch with this name already exists',
|
||||
(v) => (v && v.length <= 25) || 'Name must be less than 25 characters',
|
||||
(v) => (v && v.length >= 3) || 'Name must be at least 3 characters'
|
||||
],
|
||||
description: null,
|
||||
isEdit: false,
|
||||
pendingDelete: false
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
async createBranch() {
|
||||
this.loading = true
|
||||
await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation branchCreate($params: BranchCreateInput!) {
|
||||
branchCreate(branch: $params)
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
params: {
|
||||
streamId: this.streamId,
|
||||
name: this.name,
|
||||
description: this.description
|
||||
}
|
||||
}
|
||||
})
|
||||
this.loading = false
|
||||
this.$emit('close')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -34,11 +34,11 @@
|
||||
</v-card-actions>
|
||||
</v-form>
|
||||
<v-divider class="my-5" v-show="!showDelete" />
|
||||
<v-card-title v-show="!showDelete" class="error--text body-2">
|
||||
<v-btn block x-small outlined color="error" @click="showDelete = true">Delete Stream</v-btn>
|
||||
<v-card-title v-show="!showDelete" class="error--text body-2 pa-2">
|
||||
<v-btn block x-small text color="error" @click="showDelete = true">Delete Stream</v-btn>
|
||||
</v-card-title>
|
||||
<v-card-text v-show="showDelete" class="caption py-5">
|
||||
<h3 class="error--text">Deleting Stream {{ internalName }}</h3>
|
||||
<h2 class="error--text py-3">Deleting Stream {{ internalName }}</h2>
|
||||
<span class="error--text">
|
||||
Type the name of the stream below to confirm you really want to delete it.
|
||||
<b>You cannot undo this action.</b>
|
||||
@@ -48,6 +48,9 @@
|
||||
label="confirm stream name"
|
||||
class="pr-5"
|
||||
></v-text-field>
|
||||
</v-card-text>
|
||||
<v-card-actions v-show="showDelete">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
class="mr-3"
|
||||
color="error"
|
||||
@@ -58,7 +61,7 @@
|
||||
delete
|
||||
</v-btn>
|
||||
<v-btn @click="showDelete = false">Cancel</v-btn>
|
||||
</v-card-text>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
@@ -1,10 +1,27 @@
|
||||
<template>
|
||||
<v-card class="pa-4" color="background2">
|
||||
<v-card-title class="subtitle-1">Edit Description</v-card-title>
|
||||
<v-card-text>
|
||||
<v-card :loading="loading">
|
||||
<template slot="progress">
|
||||
<v-progress-linear indeterminate></v-progress-linear>
|
||||
</template>
|
||||
<v-card-title>Edit Description</v-card-title>
|
||||
<v-card-text class="py-0 my-0">
|
||||
<v-row>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<p>Markdown is enabled.</p>
|
||||
<p class="caption">
|
||||
Use Markdown! Tips:
|
||||
<code>#, ##, ###</code>
|
||||
prefix headings, links:
|
||||
<code>[speckle](https://speckle.systems)</code>
|
||||
, images:
|
||||
<code></code>
|
||||
, list items are prefixed by
|
||||
<code>-</code>
|
||||
on new lines,
|
||||
<b>bold</b>
|
||||
text by surrounding it with
|
||||
<code>**</code>
|
||||
, etc.
|
||||
</p>
|
||||
<v-textarea
|
||||
v-model="innerStreamDescription"
|
||||
auto-grow
|
||||
@@ -14,13 +31,13 @@
|
||||
></v-textarea>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="6">
|
||||
<p>Preview</p>
|
||||
<p class="caption">Preview</p>
|
||||
<div class="marked-preview" v-html="compiledMarkdown"></div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn @click.native="save">Save & Close</v-btn>
|
||||
<v-card-actions class="pb-10">
|
||||
<v-btn block @click.native="save">Save & Close</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
@@ -31,37 +48,33 @@ import gql from 'graphql-tag'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
id: String,
|
||||
id: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data: () => ({
|
||||
innerStreamDescription: null
|
||||
}),
|
||||
data() {
|
||||
return {
|
||||
innerStreamDescription: this.description,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
compiledMarkdown() {
|
||||
if (!this.innerStreamDescription) return ''
|
||||
let md = marked(this.innerStreamDescription)
|
||||
return DOMPurify.sanitize(md)
|
||||
},
|
||||
streamDescription: {
|
||||
get() {
|
||||
return this.innerStreamDescription
|
||||
},
|
||||
set(value) {
|
||||
this.innerStreamDescription = value
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.innerStreamDescription = this.description
|
||||
},
|
||||
methods: {
|
||||
async save() {
|
||||
this.loading = true
|
||||
try {
|
||||
this.$apollo.mutate({
|
||||
await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation editDescription($input: StreamUpdateInput!) {
|
||||
streamUpdate(stream: $input)
|
||||
@@ -74,10 +87,11 @@ export default {
|
||||
}
|
||||
}
|
||||
})
|
||||
this.$emit('close', this.innerStreamDescription)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
this.loading = false
|
||||
this.$emit('close')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<v-card class="" color="background2" :loading="loading">
|
||||
<v-card :loading="loading">
|
||||
<template slot="progress">
|
||||
<v-progress-linear indeterminate></v-progress-linear>
|
||||
</template>
|
||||
|
||||
@@ -15,15 +15,6 @@ query Stream($id: String!) {
|
||||
}
|
||||
branches {
|
||||
totalCount
|
||||
cursor
|
||||
items {
|
||||
id
|
||||
name
|
||||
description
|
||||
commits {
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
}
|
||||
commits {
|
||||
totalCount
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
query StreamBranches($id: String!) {
|
||||
stream(id: $id) {
|
||||
id
|
||||
branches {
|
||||
totalCount
|
||||
items{
|
||||
id
|
||||
name
|
||||
description
|
||||
commits {
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
query Stream($id: String!) {
|
||||
query StreamCommits($id: String!) {
|
||||
stream(id: $id) {
|
||||
id
|
||||
commits {
|
||||
|
||||
@@ -12,6 +12,12 @@
|
||||
<v-card-text>
|
||||
{{ branch.description }}
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-btn small @click="dialogEdit = true">Edit</v-btn>
|
||||
<v-dialog v-model="dialogEdit" max-width="500">
|
||||
<branch-edit-dialog :branch="branch" @close="closeEdit" />
|
||||
</v-dialog>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
<v-card>
|
||||
<v-expansion-panels flat focusable>
|
||||
@@ -22,9 +28,8 @@
|
||||
<v-expansion-panel-content>
|
||||
<p class="caption mt-4">
|
||||
<b>Grasshopper & Dynamo:</b>
|
||||
Copy and paste this page's url into a text panel and connect
|
||||
that to the "Stream" input of a receiver component or sender
|
||||
component.
|
||||
Copy and paste this page's url into a text panel and connect that to the "Stream"
|
||||
input of a receiver component or sender component.
|
||||
<b>Senders</b>
|
||||
will push commits to this branch, whereas
|
||||
<b>receivers</b>
|
||||
@@ -43,15 +48,16 @@
|
||||
</v-expansion-panels>
|
||||
</v-card>
|
||||
<v-card class="pa-4" elevation="0" rounded="lg" color="background2">
|
||||
<v-subheader class="text-uppercase">
|
||||
Commits ({{ branch.commits.totalCount }})
|
||||
</v-subheader>
|
||||
<v-subheader class="text-uppercase">Commits ({{ branch.commits.totalCount }})</v-subheader>
|
||||
<v-card-text v-if="branch.commits.totalCount === 0">
|
||||
It's a bit lonely here: there are no commits on this branch.
|
||||
</v-card-text>
|
||||
<v-card-text>
|
||||
<list-item-commit
|
||||
v-for="item in branch.commits.items"
|
||||
:key="item.id"
|
||||
:commit="item"
|
||||
:stream-id="stream.id"
|
||||
:stream-id="streamId"
|
||||
></list-item-commit>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
@@ -59,29 +65,21 @@
|
||||
</v-row>
|
||||
</template>
|
||||
<script>
|
||||
import gql from "graphql-tag"
|
||||
import SidebarStream from "../components/SidebarStream"
|
||||
import streamQuery from "../graphql/stream.gql"
|
||||
import branchQuery from "../graphql/branch.gql"
|
||||
import ListItemCommit from "../components/ListItemCommit"
|
||||
import CommitDialog from "../components/dialogs/CommitDialog"
|
||||
import gql from 'graphql-tag'
|
||||
import branchQuery from '../graphql/branch.gql'
|
||||
import ListItemCommit from '../components/ListItemCommit'
|
||||
import BranchEditDialog from '../components/dialogs/BranchEditDialog'
|
||||
|
||||
export default {
|
||||
name: "Commit",
|
||||
components: { SidebarStream, CommitDialog, ListItemCommit },
|
||||
data: () => ({ selectedBranch: 0 }),
|
||||
name: 'Branch',
|
||||
components: { ListItemCommit, BranchEditDialog },
|
||||
data() {
|
||||
return {
|
||||
dialogEdit: false
|
||||
}
|
||||
},
|
||||
apollo: {
|
||||
stream: {
|
||||
prefetch: true,
|
||||
query: streamQuery,
|
||||
variables() {
|
||||
return {
|
||||
id: this.$route.params.streamId
|
||||
}
|
||||
}
|
||||
},
|
||||
branch: {
|
||||
prefetch: true,
|
||||
query: branchQuery,
|
||||
variables() {
|
||||
return {
|
||||
@@ -89,36 +87,55 @@ export default {
|
||||
branchName: this.$route.params.branchName
|
||||
}
|
||||
},
|
||||
update: (data) => data.stream.branch
|
||||
update: (data) => data.stream.branch,
|
||||
error(error) {
|
||||
this.$router.push({ path: '/error' })
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
streamId() {
|
||||
return this.$route.params.streamId
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
editBranch() {
|
||||
this.$refs.commitDialog
|
||||
.open(this.stream.commit, this.stream.id)
|
||||
.then((dialog) => {
|
||||
if (!dialog.result) return
|
||||
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: gql`
|
||||
mutation commitUpdate($myCommit: CommitUpdateInput!) {
|
||||
commitUpdate(commit: $myCommit)
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
myCommit: { ...dialog.commit }
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
this.$apollo.queries.stream.refetch()
|
||||
})
|
||||
.catch((error) => {
|
||||
// Error
|
||||
console.error(error)
|
||||
})
|
||||
closeEdit({ name, deleted }) {
|
||||
this.dialogEdit = false
|
||||
if (deleted) {
|
||||
this.$router.push({ path: `/streams/${this.streamId}` })
|
||||
return
|
||||
}
|
||||
if (name !== this.$route.params.branchName) {
|
||||
this.$router.push({
|
||||
path: `/streams/${this.streamId}/branches/${encodeURIComponent(name)}`
|
||||
})
|
||||
return
|
||||
}
|
||||
this.$apollo.queries.branch.refetch()
|
||||
},
|
||||
editBranch() {
|
||||
this.$refs.commitDialog.open(this.stream.commit, this.stream.id).then((dialog) => {
|
||||
if (!dialog.result) return
|
||||
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: gql`
|
||||
mutation commitUpdate($myCommit: CommitUpdateInput!) {
|
||||
commitUpdate(commit: $myCommit)
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
myCommit: { ...dialog.commit }
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
this.$apollo.queries.stream.refetch()
|
||||
})
|
||||
.catch((error) => {
|
||||
// Error
|
||||
console.error(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
<template>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="3">
|
||||
<server-info-card></server-info-card>
|
||||
</v-col>
|
||||
<v-col cols="9">
|
||||
<v-row align="center" justify="center">
|
||||
<v-col cols="12" md="8">
|
||||
<v-sheet rounded="lg" class="pa-15 text-center" color="background2">
|
||||
<h1>Need Help?</h1>
|
||||
<p class="ma-10 subtitle-1 font-weight-light">
|
||||
Get free help from the
|
||||
<a href="https://discourse.speckle.works/">
|
||||
Speckle Community forum
|
||||
</a>
|
||||
<a href="https://discourse.speckle.works/">Speckle Community forum</a>
|
||||
or
|
||||
<a href="mailto:hello@speckle.systems">contact us</a>
|
||||
to set up a support agreement and one of our engineers will help
|
||||
right away 🚀!
|
||||
to set up a support agreement and one of our engineers will help right away 🚀!
|
||||
</p>
|
||||
</v-sheet>
|
||||
</v-col>
|
||||
@@ -23,7 +17,7 @@
|
||||
</v-container>
|
||||
</template>
|
||||
<script>
|
||||
import ServerInfoCard from "../components/ServerInfoCard"
|
||||
import ServerInfoCard from '../components/ServerInfoCard'
|
||||
|
||||
export default {
|
||||
components: { ServerInfoCard }
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<new-stream-dialog :open="newStreamDialog" />
|
||||
</v-dialog>
|
||||
|
||||
<v-btn href="https://twitter.com/specklesystems" target="_blank" text small>
|
||||
<v-btn href="https://twitter.com/specklesystems" target="_blank" block text>
|
||||
<v-icon small class="mr-2">mdi-twitter</v-icon>
|
||||
Speckle on Twitter!
|
||||
</v-btn>
|
||||
|
||||
@@ -7,13 +7,16 @@
|
||||
<v-col cols="12" sm="12" md="8" lg="9" xl="7">
|
||||
<v-card v-if="user" class="mb-3">
|
||||
<v-card-text class="body-1">
|
||||
You have
|
||||
<v-icon small>mdi-compare-vertical</v-icon>
|
||||
<b>{{ user.streams.totalCount }}</b>
|
||||
streams and
|
||||
<v-icon small>mdi-source-commit</v-icon>
|
||||
<b>{{ user.commits.totalCount }}</b>
|
||||
commits.
|
||||
<span>
|
||||
You have
|
||||
<v-icon small>mdi-compare-vertical</v-icon>
|
||||
<b>{{ user.streams.totalCount }}</b>
|
||||
streams and
|
||||
<v-icon small>mdi-source-commit</v-icon>
|
||||
<b>{{ user.commits.totalCount }}</b>
|
||||
commits.
|
||||
</span>
|
||||
<v-btn icon to="/streams"><v-icon>mdi-arrow-right</v-icon></v-btn>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
<v-alert type="info">Heads up! The sections below are intended for developers.</v-alert>
|
||||
|
||||
@@ -1,24 +1,11 @@
|
||||
<template>
|
||||
<v-container v-if="error">
|
||||
<v-card>
|
||||
<v-card-title>Something went wrong.</v-card-title>
|
||||
</v-card>
|
||||
</v-container>
|
||||
<v-container v-else>
|
||||
<v-row v-if="$apollo.loading">
|
||||
<v-col cols="12" sm="12" md="4" lg="3" xl="2">
|
||||
<v-skeleton-loader type="card, article"></v-skeleton-loader>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="8" lg="9" xl="7">
|
||||
<v-skeleton-loader type="article, article"></v-skeleton-loader>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-container>
|
||||
<v-row v-if="stream">
|
||||
<v-col cols="12" sm="12" md="4" lg="3" xl="2">
|
||||
<sidebar-stream :stream="stream" :user-role="userRole" @refresh="refresh"></sidebar-stream>
|
||||
<sidebar-stream :user-role="userRole"></sidebar-stream>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="12" md="8" lg="9" xl="7">
|
||||
<router-view :stream="stream" :user-role="userRole"></router-view>
|
||||
<router-view :user-role="userRole"></router-view>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
@@ -26,7 +13,6 @@
|
||||
<script>
|
||||
import SidebarStream from '../components/SidebarStream'
|
||||
import streamQuery from '../graphql/stream.gql'
|
||||
import streamCommitsQuery from '../graphql/streamCommits.gql'
|
||||
|
||||
export default {
|
||||
name: 'Stream',
|
||||
@@ -34,48 +20,19 @@ export default {
|
||||
SidebarStream
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
error: null,
|
||||
dialogDescription: false,
|
||||
selectedBranch: 0,
|
||||
stream: {
|
||||
id: null,
|
||||
branches: {
|
||||
totalCount: 0,
|
||||
items: []
|
||||
},
|
||||
commits: {
|
||||
totalCount: 0,
|
||||
items: []
|
||||
}
|
||||
},
|
||||
commits: {
|
||||
totalCount: 0,
|
||||
items: []
|
||||
}
|
||||
}
|
||||
return {}
|
||||
},
|
||||
apollo: {
|
||||
stream: {
|
||||
prefetch: true,
|
||||
query: streamQuery,
|
||||
variables() {
|
||||
return {
|
||||
id: this.$route.params.streamId
|
||||
}
|
||||
},
|
||||
error(error) {
|
||||
error() {
|
||||
this.$router.push({ path: '/error' })
|
||||
}
|
||||
},
|
||||
commits: {
|
||||
query: streamCommitsQuery,
|
||||
variables() {
|
||||
return {
|
||||
id: this.$route.params.streamId
|
||||
}
|
||||
},
|
||||
update: (data) => data.stream.commits
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -87,20 +44,6 @@ export default {
|
||||
if (contrib) return contrib.role.split(':')[1]
|
||||
else return null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
refresh() {
|
||||
this.$apollo.queries.stream.refetch()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.v-item-group {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
<template>
|
||||
<v-row>
|
||||
<v-col v-if="!stream || $apollo.loading" cols="12">
|
||||
<!-- <v-col v-if="!stream || $apollo.loading" cols="12">
|
||||
<v-skeleton-loader type="article, article"></v-skeleton-loader>
|
||||
</v-col>
|
||||
<v-col v-else sm="12">
|
||||
<v-card rounded="lg" class="pa-4 mb-4" elevation="0" color="background2">
|
||||
<v-card-title v-if="!stream.description">Description</v-card-title>
|
||||
<v-card-text v-if="!stream.description">No description provided.</v-card-text>
|
||||
</v-col> -->
|
||||
<v-col sm="12">
|
||||
<v-card v-if="$apollo.queries.description.loading">
|
||||
<v-skeleton-loader type="article"></v-skeleton-loader>
|
||||
</v-card>
|
||||
<v-card v-else rounded="lg" class="pa-4 mb-4" elevation="0" color="background2">
|
||||
<v-card-title v-if="!description">Description</v-card-title>
|
||||
<v-card-text v-if="!description">No description provided.</v-card-text>
|
||||
<v-card-text
|
||||
v-if="stream.description"
|
||||
v-if="description"
|
||||
class="marked-preview"
|
||||
v-html="compiledStreamDescription"
|
||||
></v-card-text>
|
||||
@@ -16,27 +19,33 @@
|
||||
<v-btn small @click="dialogDescription = true">Edit Description</v-btn>
|
||||
<v-dialog v-model="dialogDescription">
|
||||
<stream-description-dialog
|
||||
:id="stream.id"
|
||||
:description="stream.description"
|
||||
:id="$route.params.streamId"
|
||||
:description="description"
|
||||
@close="closeDescription"
|
||||
/>
|
||||
</v-dialog>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
|
||||
<v-card rounded="lg" class="pa-4 mb-4" elevation="0" color="background2">
|
||||
<v-card v-if="$apollo.queries.branches.loading">
|
||||
<v-skeleton-loader type="article"></v-skeleton-loader>
|
||||
</v-card>
|
||||
<v-card v-else rounded="lg" class="pa-4 mb-4" elevation="0" color="background2">
|
||||
<v-card-title>
|
||||
<v-icon class="mr-2">mdi-source-branch</v-icon>
|
||||
Branches
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
Branches allow you to manage parallel versions of data in a single stream, by organising
|
||||
them within a topic.
|
||||
A branch represents an independent line of data. You can think of them as an independent
|
||||
directory, staging area and project history.
|
||||
</v-card-text>
|
||||
<v-card-text>
|
||||
<v-list two-line color="transparent">
|
||||
<template v-for="item in branches">
|
||||
<v-list-item :key="item.id" :to="`/streams/${stream.id}/branches/${item.name}`">
|
||||
<template v-for="item in branches.items">
|
||||
<v-list-item
|
||||
:key="item.id"
|
||||
:to="`/streams/${$route.params.streamId}/branches/${encodeURIComponent(item.name)}`"
|
||||
>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>
|
||||
<b>{{ item.name }}</b>
|
||||
@@ -54,25 +63,39 @@
|
||||
</v-list-item>
|
||||
</template>
|
||||
</v-list>
|
||||
<v-btn v-if="userRole === 'contributor' || userRole === 'owner'" small @click="newBranch">
|
||||
<v-btn
|
||||
v-if="userRole === 'contributor' || userRole === 'owner'"
|
||||
small
|
||||
@click="dialogBranch = true"
|
||||
>
|
||||
new branch
|
||||
</v-btn>
|
||||
<branch-dialog ref="branchDialog" :branches="branches"></branch-dialog>
|
||||
<v-dialog v-model="dialogBranch" max-width="500">
|
||||
<new-branch-dialog
|
||||
:branch-names="branches.items.map((b) => b.name)"
|
||||
:stream-id="$route.params.streamId"
|
||||
@close="closeBranchDialog"
|
||||
/>
|
||||
</v-dialog>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-card rounded="lg" class="pa-4 mb-4" elevation="0" color="background2">
|
||||
<v-card v-if="$apollo.queries.commits.loading">
|
||||
<v-skeleton-loader type="article"></v-skeleton-loader>
|
||||
</v-card>
|
||||
|
||||
<v-card v-else rounded="lg" class="pa-4 mb-4" elevation="0" color="background2">
|
||||
<v-card-title>
|
||||
Latest activity
|
||||
<span class="font-weight-light ml-2 body-1">({{ commits.totalCount }} total)</span>
|
||||
</v-card-title>
|
||||
<v-card-text>All the commits from this stream are below.</v-card-text>
|
||||
<v-card-text v-if="stream.commits">
|
||||
<v-card-text v-if="commits">
|
||||
<list-item-commit
|
||||
v-for="item in commits.items"
|
||||
:key="item.id"
|
||||
:commit="item"
|
||||
:stream-id="stream.id"
|
||||
:stream-id="$route.params.streamId"
|
||||
></list-item-commit>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
@@ -83,15 +106,16 @@
|
||||
import marked from 'marked'
|
||||
import DOMPurify from 'dompurify'
|
||||
import gql from 'graphql-tag'
|
||||
import BranchDialog from '../components/dialogs/BranchDialog'
|
||||
import NewBranchDialog from '../components/dialogs/BranchNewDialog'
|
||||
import StreamDescriptionDialog from '../components/dialogs/StreamDescriptionDialog'
|
||||
import ListItemCommit from '../components/ListItemCommit'
|
||||
import streamCommitsQuery from '../graphql/streamCommits.gql'
|
||||
import streamBranchesQuery from '../graphql/streamBranches.gql'
|
||||
|
||||
export default {
|
||||
name: 'StreamMain',
|
||||
components: {
|
||||
BranchDialog,
|
||||
NewBranchDialog,
|
||||
ListItemCommit,
|
||||
StreamDescriptionDialog
|
||||
},
|
||||
@@ -108,6 +132,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
dialogDescription: false,
|
||||
dialogBranch: false,
|
||||
selectedBranch: 0
|
||||
}
|
||||
},
|
||||
@@ -120,123 +145,62 @@ export default {
|
||||
}
|
||||
},
|
||||
update: (data) => data.stream.commits
|
||||
},
|
||||
branches: {
|
||||
query: streamBranchesQuery,
|
||||
variables() {
|
||||
return {
|
||||
id: this.$route.params.streamId
|
||||
}
|
||||
},
|
||||
update(data) {
|
||||
data.stream.branches.items = data.stream.branches.items.reverse()
|
||||
return data.stream.branches
|
||||
}
|
||||
},
|
||||
description: {
|
||||
query: gql`
|
||||
query($id: String!) {
|
||||
stream(id: $id) {
|
||||
id
|
||||
description
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables() {
|
||||
return {
|
||||
id: this.$route.params.streamId
|
||||
}
|
||||
},
|
||||
update: (data) => data.stream.description
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
compiledStreamDescription() {
|
||||
if (!this.stream.description) return ''
|
||||
let md = marked(this.stream.description)
|
||||
return DOMPurify.sanitize(md)
|
||||
branchNames() {
|
||||
if (!this.branches) return []
|
||||
return this.branches.items.map((b) => b.name)
|
||||
},
|
||||
branches() {
|
||||
//reverse without changing original array
|
||||
return this.stream.branches.items.slice().reverse()
|
||||
compiledStreamDescription() {
|
||||
if (!this.description) return ''
|
||||
let md = marked(this.description)
|
||||
return DOMPurify.sanitize(md)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$matomo && this.$matomo.trackPageView('streams/single')
|
||||
this.$apollo.queries.branches.refetch()
|
||||
this.$apollo.queries.description.refetch()
|
||||
this.$apollo.queries.commits.refetch()
|
||||
},
|
||||
methods: {
|
||||
closeDescription(newDescription) {
|
||||
this.stream.description = newDescription
|
||||
closeDescription() {
|
||||
this.dialogDescription = false
|
||||
this.$apollo.queries.description.refetch()
|
||||
},
|
||||
newBranch() {
|
||||
this.$refs.branchDialog.open().then((dialog) => {
|
||||
if (!dialog.result) return
|
||||
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: gql`
|
||||
mutation branchCreate($myBranch: BranchCreateInput!) {
|
||||
branchCreate(branch: $myBranch)
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
myBranch: {
|
||||
streamId: this.stream.id,
|
||||
...dialog.branch
|
||||
}
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
// Result
|
||||
console.log(data)
|
||||
|
||||
this.$apollo.queries.stream.refetch()
|
||||
})
|
||||
.catch((error) => {
|
||||
// Error
|
||||
console.error(error)
|
||||
// We restore the initial user input
|
||||
//this.newTag = newTag
|
||||
})
|
||||
})
|
||||
},
|
||||
editBranch() {
|
||||
this.$refs.branchDialog
|
||||
.open(this.branches[this.selectedBranch], this.stream.id)
|
||||
.then((dialog) => {
|
||||
if (!dialog.result) return
|
||||
|
||||
//DELETE BRANCH
|
||||
if (dialog.delete) {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: gql`
|
||||
mutation branchDelete($myBranch: BranchDeleteInput!) {
|
||||
branchDelete(branch: $myBranch)
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
myBranch: {
|
||||
id: this.branches[this.selectedBranch].id,
|
||||
streamId: this.stream.id
|
||||
}
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
this.selectedBranch = 0
|
||||
this.$apollo.queries.stream.refetch()
|
||||
})
|
||||
.catch((error) => {
|
||||
// Error
|
||||
console.error(error)
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
//EDIT BRANCH
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: gql`
|
||||
mutation branchUpdate($myBranch: BranchUpdateInput!) {
|
||||
branchUpdate(branch: $myBranch)
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
myBranch: { ...dialog.branch }
|
||||
}
|
||||
})
|
||||
.then((data) => {
|
||||
this.$apollo.queries.stream.refetch()
|
||||
})
|
||||
.catch((error) => {
|
||||
// Error
|
||||
console.error(error)
|
||||
})
|
||||
})
|
||||
closeBranchDialog() {
|
||||
this.dialogBranch = false
|
||||
this.$apollo.queries.branches.refetch()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.v-item-group {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user