Moved Modal t set instance url to a separate dedicated file

SetInstanceModal.vue
This commit is contained in:
jovuit 2019-02-25 14:03:53 +01:00 committed by Eliot Berriot
parent 8beb08d677
commit 1d7ad6978a
15 changed files with 205 additions and 103 deletions

View File

@ -0,0 +1,3 @@
Better workflow for connecting to another instance (#715)
Changing the instance used is now better integrated in the App, and it is checked that the chosen instance and the suggested instances are valid and running Funkwhale servers.

View File

@ -1,5 +1,5 @@
<template>
<div id="app">
<div id="app" :key="String($store.state.instance.instanceUrl)">
<!-- here, we display custom stylesheets, if any -->
<link
v-for="url in customStylesheets"
@ -8,40 +8,16 @@
:href="url"
:key="url"
>
<div class="ui main text container instance-chooser" v-if="!$store.state.instance.instanceUrl">
<div class="ui padded segment">
<h1 class="ui header">
<translate>Choose your instance</translate>
</h1>
<form class="ui form" @submit.prevent="$store.dispatch('instance/setUrl', instanceUrl)">
<p>
<translate>You need to select an instance in order to continue</translate>
</p>
<div class="ui action input">
<input type="text" v-model="instanceUrl">
<button type="submit" class="ui button">
<translate>Submit</translate>
</button>
</div>
<p>
<translate>Suggested choices</translate>
</p>
<div class="ui bulleted list">
<div class="ui item" v-for="url in suggestedInstances">
<a @click="instanceUrl = url; $store.dispatch('instance/setUrl', url)">{{ url }}</a>
</div>
</div>
</form>
</div>
</div>
<template v-else>
<template>
<sidebar></sidebar>
<set-instance-modal @update:show="showSetInstanceModal = $event" :show="showSetInstanceModal"></set-instance-modal>
<service-messages v-if="messages.length > 0"/>
<router-view :key="$route.fullPath"></router-view>
<div class="ui fitted divider"></div>
<app-footer
:version="version"
@show:shortcuts-modal="showShortcutsModal = !showShortcutsModal"
@show:set-instance-modal="showSetInstanceModal = !showSetInstanceModal"
></app-footer>
<playlist-modal v-if="$store.state.auth.authenticated"></playlist-modal>
<filter-modal v-if="$store.state.auth.authenticated"></filter-modal>
@ -66,6 +42,7 @@ import locales from './locales'
import PlaylistModal from '@/components/playlists/PlaylistModal'
import FilterModal from '@/components/moderation/FilterModal'
import ShortcutsModal from '@/components/ShortcutsModal'
import SetInstanceModal from '@/components/SetInstanceModal'
export default {
name: 'app',
@ -76,7 +53,8 @@ export default {
PlaylistModal,
ShortcutsModal,
GlobalEvents,
ServiceMessages
ServiceMessages,
SetInstanceModal,
},
data () {
return {
@ -84,6 +62,7 @@ export default {
nodeinfo: null,
instanceUrl: null,
showShortcutsModal: false,
showSetInstanceModal: false,
}
},
created () {
@ -131,12 +110,6 @@ export default {
self.nodeinfo = response.data
})
},
switchInstance () {
let confirm = window.confirm(this.$gettext('This will erase your local data and disconnect you, do you want to continue?'))
if (confirm) {
this.$store.commit('instance/instanceUrl', null)
}
},
autodetectLanguage () {
let userLanguage = navigator.language || navigator.userLanguage
let available = locales.locales.map(e => { return e.code })
@ -257,7 +230,6 @@ export default {
console.log('No momentjs locale available for', shortLocale)
})
})
console.log(moment.locales())
}
}
}

View File

@ -13,9 +13,9 @@
<div class="item" v-if="version">
<translate :translate-params="{version: version}" >Version %{version}</translate>
</div>
<a @click="switchInstance" class="item" >
<div role="button" class="item" @click="$emit('show:set-instance-modal')" >
<translate>Use another instance</translate>
</a>
</div>
</div>
<div class="ui form">
<div class="ui field">
@ -66,18 +66,6 @@ import axios from 'axios'
export default {
props: ["version"],
methods: {
switchInstance() {
let confirm = window.confirm(
this.$gettext(
"This will erase your local data and disconnect you, do you want to continue?"
)
)
if (confirm) {
this.$store.commit("instance/instanceUrl", null)
}
}
},
computed: {
...mapState({
messages: state => state.ui.messages
@ -88,13 +76,6 @@ export default {
parser.href = url
return parser.hostname
},
suggestedInstances() {
let instances = [
this.$store.getters["instance/defaultUrl"](),
"https://demo.funkwhale.audio"
]
return instances
}
}
}
</script>

View File

@ -1,21 +1,21 @@
<template>
<svg version="1.1" id="layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 141.7 141.7" enable-background="new 0 0 141.7 141.7" xml:space="preserve">
viewBox="0 0 141.7 141.7" enable-background="new 0 0 141.7 141.7" xml:space="preserve">
<g>
<g>
<path :fill="fill" d="M70.9,86.1c11.7,0,21.2-9.5,21.2-21.2c0-0.6-0.5-1.1-1.1-1.1h-8c-0.6,0-1.1,0.5-1.1,1.1c0,6-4.9,11-11,11
c-6,0-11-4.9-11-11c0-0.6-0.5-1.1-1.1-1.1h-8c-0.6,0-1.1,0.5-1.1,1.1C49.7,76.6,59.2,86.1,70.9,86.1z"/>
<path :fill="fill" d="M70.9,106.1c22.7,0,41.2-18.5,41.2-41.2c0-0.6-0.5-1.1-1.1-1.1h-8c-0.6,0-1.1,0.5-1.1,1.1
c0,17.1-13.9,31-31,31c-17.1,0-31-13.9-31-31c0-0.6-0.5-1.1-1.1-1.1h-8c-0.6,0-1.1,0.5-1.1,1.1C29.6,87.6,48.1,106.1,70.9,106.1z"
/>
<path :fill="fill" d="M131.1,63.8h-8c-0.6,0-1.1,0.5-1.1,1.1C122,93.1,99,116,70.9,116c-28.2,0-51.1-22.9-51.1-51.1
c0-0.6-0.5-1.1-1.1-1.1h-8c-0.6,0-1.1,0.5-1.1,1.1c0,33.8,27.5,61.3,61.3,61.3c33.8,0,61.3-27.5,61.3-61.3
C132.2,64.3,131.7,63.8,131.1,63.8z"/>
</g>
<path :fill="fill" d="M43.3,37.3c4.1,2.1,8.5,2.5,12.5,4.8c2.6,1.5,4.2,3.2,5.8,5.7c2.5,3.8,2.4,8.5,2.4,8.5l0.3,5.2
c0,0,2,5.2,6.4,5.2c4.7,0,6.4-5.2,6.4-5.2l0.3-5.2c0,0-0.1-4.7,2.4-8.5c1.6-2.5,3.2-4.3,5.8-5.7c4-2.3,8.4-2.7,12.5-4.8
c4.1-2.1,8.1-4.8,10.8-8.6c2.7-3.8,4-8.8,2.5-13.2c-7.8-0.4-16.8,0.5-23.7,4.2c-9.6,5.1-15.4,3.3-17.1,10.9h-0.1
c-1.7-7.7-7.5-5.8-17.1-10.9c-6.9-3.7-15.9-4.6-23.7-4.2c-1.5,4.4-0.2,9.4,2.5,13.2C35.2,32.5,39.2,35.2,43.3,37.3z"/>
<g>
<path :fill="fill" d="M70.9,86.1c11.7,0,21.2-9.5,21.2-21.2c0-0.6-0.5-1.1-1.1-1.1h-8c-0.6,0-1.1,0.5-1.1,1.1c0,6-4.9,11-11,11
c-6,0-11-4.9-11-11c0-0.6-0.5-1.1-1.1-1.1h-8c-0.6,0-1.1,0.5-1.1,1.1C49.7,76.6,59.2,86.1,70.9,86.1z"/>
<path :fill="fill" d="M70.9,106.1c22.7,0,41.2-18.5,41.2-41.2c0-0.6-0.5-1.1-1.1-1.1h-8c-0.6,0-1.1,0.5-1.1,1.1
c0,17.1-13.9,31-31,31c-17.1,0-31-13.9-31-31c0-0.6-0.5-1.1-1.1-1.1h-8c-0.6,0-1.1,0.5-1.1,1.1C29.6,87.6,48.1,106.1,70.9,106.1z"
/>
<path :fill="fill" d="M131.1,63.8h-8c-0.6,0-1.1,0.5-1.1,1.1C122,93.1,99,116,70.9,116c-28.2,0-51.1-22.9-51.1-51.1
c0-0.6-0.5-1.1-1.1-1.1h-8c-0.6,0-1.1,0.5-1.1,1.1c0,33.8,27.5,61.3,61.3,61.3c33.8,0,61.3-27.5,61.3-61.3
C132.2,64.3,131.7,63.8,131.1,63.8z"/>
</g>
<path :fill="fill" d="M43.3,37.3c4.1,2.1,8.5,2.5,12.5,4.8c2.6,1.5,4.2,3.2,5.8,5.7c2.5,3.8,2.4,8.5,2.4,8.5l0.3,5.2
c0,0,2,5.2,6.4,5.2c4.7,0,6.4-5.2,6.4-5.2l0.3-5.2c0,0-0.1-4.7,2.4-8.5c1.6-2.5,3.2-4.3,5.8-5.7c4-2.3,8.4-2.7,12.5-4.8
c4.1-2.1,8.1-4.8,10.8-8.6c2.7-3.8,4-8.8,2.5-13.2c-7.8-0.4-16.8,0.5-23.7,4.2c-9.6,5.1-15.4,3.3-17.1,10.9h-0.1
c-1.7-7.7-7.5-5.8-17.1-10.9c-6.9-3.7-15.9-4.6-23.7-4.2c-1.5,4.4-0.2,9.4,2.5,13.2C35.2,32.5,39.2,35.2,43.3,37.3z"/>
</g>
</svg>

View File

@ -0,0 +1,146 @@
<template>
<modal @update:show="$emit('update:show', $event); isError = false" :show="show">
<div class="header"><translate :translate-context="'Popup/Instance/Title'">Choose your instance</translate></div>
<div class="scrolling content">
<div v-if="isError" class="ui negative message">
<div class="header"><translate :translate-context="'Popup/Instance/Error message.Title'">It is not possible to connect to the given URL</translate></div>
<ul class="list">
<li><translate :translate-context="'Popup/Instance/Error message.List item'">The server might be down</translate></li>
<li><translate :translate-context="'Popup/Instance/Error message.List item'">The given address is not a Funkwhale server</translate></li>
</ul>
</div>
<form class="ui form" @submit.prevent="checkAndSwitch(instanceUrl)">
<p v-if="$store.state.instance.instanceUrl" class="description" :translate-context="'Popup/Login/Paragraph'" v-translate="{url: $store.state.instance.instanceUrl, hostname: instanceHostname }">
You are currently connected to <a href="%{ url }" target="_blank">%{ hostname }&nbsp;<i class="external icon"></i></a>. If you continue, you will be disconnected from your current instance and all your local data will be deleted.
</p>
<p v-else>
<translate :translate-context="'Popup/Instance/Paragraph'">To continue, please select the Funkwhale instance you want to connect to. Enter the address directly, or select one of the suggested choices.</translate>
</p>
<div class="field">
<label><translate :translate-context="'Popup/Instance/Input.Label/Noun'">Instance URL</translate></label>
<div class="ui action input">
<input type="text" v-model="instanceUrl" placeholder="https://funkwhale.server">
<button type="submit" :class="['ui', 'icon', {loading: isLoading}, 'button']">
<translate :translate-context="'*/*/Button.Label/Verb'">Submit</translate>
</button>
</div>
</div>
</form>
<div class="ui hidden divider"></div>
<form class="ui form" @submit.prevent="">
<div class="field">
<label><translate :translate-context="'Popup/Instance/List.Label'">Suggested choices</translate></label>
<button v-for="url in suggestedInstances" @click="checkAndSwitch(url)" class="ui basic button">{{ url }}</button>
</div>
</form>
</div>
<div class="actions">
<div class="ui cancel button"><translate :translate-context="'*/*/Button.Label/Verb'">Cancel</translate></div>
</div>
</modal>
</template>
<script>
import Modal from '@/components/semantic/Modal'
import axios from 'axios'
export default {
props: ['show'],
components: {
Modal,
},
data() {
return {
instanceUrl: null,
nodeinfo: null,
isError: false,
isLoading: false,
path: 'api/v1/instance/nodeinfo/2.0/',
}
},
methods: {
fetchNodeInfo () {
let self = this
axios.get('instance/nodeinfo/2.0/').then(response => {
self.nodeinfo = response.data
})
},
fetchUrl (url) {
let urlFetch = url
if (!urlFetch.endsWith('/')) {
urlFetch = `${urlFetch}/${this.path}`
} else {
urlFetch = `${urlFetch}${this.path}`
}
if (!urlFetch.startsWith('https://') && !urlFetch.startsWith('http://')) {
urlFetch = `https://${urlFetch}`
}
return urlFetch
},
requestDistantNodeInfo (url) {
var self = this
axios.get(this.fetchUrl(url)).then(function (response) {
self.isLoading = false
if(!url.startsWith('https://') && !url.startsWith('http://')) {
url = `https://${url}`
}
self.switchInstance(url)
}).catch(function (error) {
self.isLoading = false
self.isError = true
})
},
switchInstance (url) {
// Here we disconnect from the current instance and reconnect to the new one. No check is performed...
this.$emit('update:show', false)
this.isError = false
let msg = this.$pgettext('*/Instance/Message', 'You are now using the Funkwhale instance at %{ url }')
this.$store.commit('ui/addMessage', {
content: this.$gettextInterpolate(msg, {url: url}),
date: new Date()
})
let self = this
this.$nextTick(() => {
self.$store.commit('instance/instanceUrl', null)
self.$store.dispatch('instance/setUrl', url)
})
},
checkAndSwitch (url) {
// First we have to check if the address is a valid FW server. If yes, we switch:
this.isError = false // Clear error message if any...
this.isLoading = true
this.requestDistantNodeInfo(url)
},
},
computed: {
suggestedInstances () {
let instances = this.$store.state.instance.knownInstances.slice(0)
if (this.$store.state.instance.frontSettings.defaultServerUrl) {
let serverUrl = this.$store.state.instance.frontSettings.defaultServerUrl
if (!serverUrl.endsWith('/')) {
serverUrl = serverUrl + '/'
}
instances.push(serverUrl)
}
let self = this
instances.push(this.$store.getters['instance/defaultUrl'](), 'https://demo.funkwhale.audio/')
return _.uniq(instances.filter((e) => {return e != self.$store.state.instance.instanceUrl}))
},
instanceHostname() {
let url = this.$store.state.instance.instanceUrl
let parser = document.createElement("a")
parser.href = url
return parser.hostname
},
},
watch: {
'$store.state.instance.instanceUrl' () {
this.$store.dispatch('instance/fetchSettings')
this.fetchNodeInfo()
},
},
}
</script>
<style scoped>
</style>

View File

@ -17,14 +17,14 @@
</template>
</div>
<div class="field">
<button @click="copy" class="ui right teal labeled icon floated button"><i class="copy icon"></i><translate :translate-context="'Popup/*/Button.Label/Verb'">Copy</translate></button>
<button @click="copy" class="ui right teal labeled icon floated button"><i class="copy icon"></i><translate :translate-context="'Popup/*/Button.Label/Verb'">Copy</translate></button>
<label for="embed-width"><translate :translate-context="'Popup/Embed/Input.Label/Noun'">Embed code</translate></label>
<p><translate :translate-context="'Popup/Embed/Paragraph'">Copy/paste this code in your website HTML</translate></p>
<textarea ref="textarea":value="embedCode" rows="5" readonly>
</textarea>
<div class="ui right">
<p class="message" v-if=copied><translate :translate-context="'Content/*/Paragraph'">Text copied to clipboard!</translate></p>
</div>
<div class="ui right">
<p class="message" v-if=copied><translate :translate-context="'Content/*/Paragraph'">Text copied to clipboard!</translate></p>
</div>
</div>
</div>
</div>
@ -44,7 +44,7 @@ export default {
width: null,
height: 150,
minHeight: 100,
copied: false
copied: false
}
if (this.type === 'album') {
d.height = 330
@ -73,11 +73,11 @@ export default {
copy () {
this.$refs.textarea.select()
document.execCommand("Copy")
let self = this
self.copied = true
this.timeout = setTimeout(() => {
self.copied = false
}, 5000)
let self = this
self.copied = true
this.timeout = setTimeout(() => {
self.copied = false
}, 5000)
}
}
}
@ -86,8 +86,8 @@ export default {
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.message {
position: absolute;
right: 0;
bottom: -2em;
position: absolute;
right: 0;
bottom: -2em;
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<span :title="title" :class="['ui', {'tiny': discrete}, {'icon': !discrete}, {'buttons': !dropdownOnly && !iconOnly}]">
<span :title="title" :class="['ui', {'tiny': discrete}, {'icon': !discrete}, {'buttons': !dropdownOnly && !iconOnly}]">
<button
v-if="!dropdownOnly"
:title="labels.playNow"

View File

@ -12,7 +12,7 @@
translate-plural="%{ count } favorites"
:translate-n="$store.state.favorites.count"
:translate-params="{count: results.count}"
:translate-context="'Content/Favorites/Title'">
:translate-context="'Content/Favorites/Title'">
1 favorite
</translate>
</h2>

View File

@ -63,7 +63,7 @@
tag="h2"
class="left floated"
:translate-params="{number: disc_number + 1}"
:translate-context="'Content/Album/'"
:translate-context="'Content/Album/'"
>Volume %{ number }</translate>
<play-button class="right floated orange" :tracks="tracks">
<translate :translate-context="'Content/*/Button.Label/Verb, Short'">Play all</translate>

View File

@ -34,7 +34,7 @@
<p><translate :translate-context="'Content/Library/Paragraph'">You are about to upload music to your library. Before proceeding, please ensure that:</translate></p>
<ul>
<li v-if="library.privacy_level != 'me'">
<translate :translate-context="'Content/Library/List item'">You are not uploading copyrighted content in a public library, otherwise you may be infringing the law</translate>
<translate :translate-context="'Content/Library/List item'">You are not uploading copyrighted content in a public library, otherwise you may be infringing the law</translate>
</li>
<li>
<translate :translate-context="'Content/Library/List item'">The music files you are uploading are tagged properly.</translate>&nbsp;
@ -217,17 +217,17 @@ export default {
},
computed: {
labels() {
let denied = this.$pgettext('Content/Library/Help text',
let denied = this.$pgettext('Content/Library/Help text',
"Upload denied, ensure the file is not too big and that you have not reached your quota"
);
let server = this.$pgettext('Content/Library/Help text',
let server = this.$pgettext('Content/Library/Help text',
"Cannot upload this file, ensure it is not too big"
);
let network = this.$pgettext('Content/Library/Help text',
let network = this.$pgettext('Content/Library/Help text',
"A network error occured while uploading this file"
);
let timeout = this.$pgettext('Content/Library/Help text', "Upload timeout, please try again");
let extension = this.$pgettext('Content/Library/Help text',
let extension = this.$pgettext('Content/Library/Help text',
"Invalid file type, ensure you are uploading an audio file. Supported file extensions are %{ extensions }"
);
return {

View File

@ -30,7 +30,7 @@
<input id="public" type="checkbox" v-model="isPublic" />
<label for="public"><translate :translate-context="'Content/Radio/Checkbox.Label/Verb'">Display publicly</translate></label>
</div>
<div class="ui hidden divider"></div>
<div class="ui hidden divider"></div>
<button :disabled="!canSave" @click="save" :class="['ui', 'green', {loading: isLoading}, 'button']">
<translate :translate-context="'Content/Radio/Button.Label/Verb'">Save</translate>
</button>

View File

@ -42,7 +42,7 @@ export default {
return {
libraryFollowMessage,
libraryAcceptFollowMessage,
libraryPendingFollowMessage,
libraryPendingFollowMessage,
markRead: this.$gettext('Mark as read'),
markUnread: this.$gettext('Mark as unread'),
@ -57,18 +57,18 @@ export default {
if (a.type === 'Follow') {
if (a.object && a.object.type === 'music.Library') {
let action = null
let message = null
let message = null
if (!a.related_object.approved) {
message = this.labels.libraryPendingFollowMessage
message = this.labels.libraryPendingFollowMessage
action = {
buttonClass: 'green',
icon: 'check',
label: this.$gettext('Approve'),
handler: () => { self.approveLibraryFollow(a.related_object) }
}
} else {
message = this.labels.libraryFollowMessage
}
} else {
message = this.labels.libraryFollowMessage
}
return {
action,
detailUrl: {name: 'content.libraries.detail', params: {id: a.object.uuid}},

View File

@ -15,7 +15,7 @@
</div>
<div class="extra content">
<user-link v-if="radio.user" :user="radio.user" class="left floated" />
<div class="ui hidden divider"></div>
<div class="ui hidden divider"></div>
<radio-button class="right floated button" :type="type" :custom-radio-id="customRadioId"></radio-button>
<router-link
class="ui basic yellow button right floated"

View File

@ -48,7 +48,7 @@
</h3>
</header>
<p><translate :translate-context="'Content/Moderation/Card.Paragraph'">Moderation policies help you control how your instance interact with a given domain or account.</translate></p>
<button @click="showPolicyForm = true" class="ui primary button"><translate :translate-context="'Content/Moderation/Button/Verb'">Add a moderation policy</translate></button>
<button @click="showPolicyForm = true" class="ui primary button"><translate :translate-context="'Content/Moderation/Button/Verb'">Add a moderation policy</translate></button>
</template>
<instance-policy-card v-else-if="policy && !showPolicyForm" :object="policy" @update="showPolicyForm = true">
<header class="ui header">

View File

@ -14,7 +14,7 @@
translate-plural="Playlist containing %{ count } tracks, by %{ username }"
:translate-n="playlist.tracks_count"
:translate-params="{count: playlist.tracks_count, username: playlist.user.username}"
:translate-context="'Content/Playlist/Header.Subtitle'">
:translate-context="'Content/Playlist/Header.Subtitle'">
Playlist containing %{ count } track, by %{ username }
</translate><br>
<duration :seconds="playlist.duration" />