Verified Commit 65a10d19 authored by Agate's avatar Agate 💬

First complete translate guide

parent 702cf088
* text=auto
front/public/assets/guides/**.* filter=lfs diff=lfs merge=lfs -text
front/public/assets/guides/**/*.png filter=lfs diff=lfs merge=lfs -text
front/public/assets/guides/*/*.png filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
......@@ -12,14 +12,17 @@
"bulma": "^0.7.1",
"fork-awesome": "^1.1.5",
"moment": "^2.22.2",
"vue": "^2.5.17"
"vue": "^2.5.17",
"vue-router": "^3.0.1"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.0.0",
"@vue/cli-plugin-eslint": "^3.0.0",
"@vue/cli-service": "^3.0.0",
"css-loader": "^1.0.0",
"style-loader": "^0.23.0",
"node-sass": "^4.10.0",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"vue-template-compiler": "^2.5.17"
},
"eslintConfig": {
......
front/public/logo-full.png

40 KB | W: | H:

front/public/logo-full.png

130 Bytes | W: | H:

front/public/logo-full.png
front/public/logo-full.png
front/public/logo-full.png
front/public/logo-full.png
  • 2-up
  • Swipe
  • Onion skin
<template>
<div id="app">
<section class="section">
<div class="text container">
<img src="/logo-full.png" alt="Funkwhale logo">
<hr class="hidden">
<h1 class="title">How to contribute?</h1>
<p>Do you want to contribute but don't know where to start? Tell us how much time you can spend and we'll get you started!</p>
<hr class="hidden">
<div class="field">
<label class="label">I can spend</label>
<div class="control">
<label class="radio">
<input type="radio" name="time" :value="null" v-model="availableTime">
I don't know
</label>
<label class="radio">
<input type="radio" name="time" :value="5" v-model="availableTime">
5 minutes
</label>
<label class="radio">
<input type="radio" name="time" :value="15" v-model="availableTime">
15 minutes
</label>
<label class="radio">
<input type="radio" name="time" :value="30" v-model="availableTime">
30 minutes
</label>
<label class="radio">
<input type="radio" name="time" :value="60" v-model="availableTime">
1 hour
</label>
<label class="radio">
<input type="radio" name="time" :value="120" v-model="availableTime">
2 hours
</label>
<label class="radio">
<input type="radio" name="time" :value="9999" v-model="availableTime">
More
</label>
</div>
</div>
<hr class="hidden">
<div>
<h3 class="subtitle">Here is a list of suggestions</h3>
<div class="tasks">
<task v-for="t in suggestedTasks" :task="t" />
</div>
</div>
</div>
<div class="text container contributions" v-if="stats" >
<h1 class="title">Contribution logs</h1>
<div class="filters">
<div class="field">
<label class="label">Type</label>
<div class="control">
<div class="select">
<select v-model="filters.type">
<option
:key="option.value"
v-for="option in typeOptions"
:value="option.value">
{{ option.label }}
<template v-if="option.value">({{ stats.types[option.value] }})</template>
</option>
</select>
</div>
</div>
</div>
</div>
<hr />
<component
:is="getComponent(contribution.type)"
:key="contribution.key"
:contribution="contribution"
@toggle-modal="toggleModal"
v-for="contribution in contributions"></component>
<button @click="fetch(true)" v-if="next" class="button is-medium is-fullwidth">Load more</button>
</div>
</section>
<div class="modals">
<modal :show="modals['donation']" @close="modals['donation'] = false">
<template slot="title">Hello world</template>
</modal>
</div>
<router-view></router-view>
</div>
</template>
<script>
import axios from "axios";
import Task from "@/components/Task";
import ContributionItem from "@/components/ContributionItem";
import Issue from "@/components/Issue";
import Donation from "@/components/Donation";
import Modal from "@/components/Modal";
export default {
components: {
ContributionItem,
Donation,
Issue,
Modal,
Task
},
data() {
return {
modals: {
donation: false,
"dev:issue": false
},
contributions: [],
filters: {
type: null
},
next: null,
stats: null,
availableTime: null,
typeOptions: [
{ value: null, label: "All" },
{ value: "donation", label: "Donations" },
{ value: "dev:issue", label: "Development/Issue" }
],
availableTasks: [
{
name: "Become a backer",
category: 5,
icon: "eur",
summary: "Support Funkwhale financially, on a one-time or recurring basis."
},
{
name: "Talk about Funkwhale",
category: 5,
icon: "share",
summary: "Increase the awareness of Funkwhale within your circle."
},
{
name: "Share your feedback",
category: 5,
icon: "comment",
summary: "How happy are you with the project? Let us know!"
},
{
name: "Join the discussion",
category: 15,
icon: "comment",
summary: "Help us shape the future of Funkwhale and go in the right direction."
},
{
name: "Translate Funkwhale",
category: 15,
icon: "language",
summary: "Help translate the project in other languages."
},
{
name: "Report a bug",
icon: "bug",
category: 15,
summary: "Report us typos, display issues, or other unexpected bebaviour so we can fix it."
},
{
name: "Review the documentation",
icon: "search",
category: 15,
summary: "Ensure our documentation is accurate, understandable and up-to-date."
},
{
name: "Publish audio content",
icon: "music",
category: 15,
summary: "Upload content with an open licence, or your own work, on the network."
},
{
name: "Submit patches for the documentation",
icon: "pencil",
category: 30,
summary: "Fix issues and improve the quality of our documentation."
},
{
name: "Request a feature",
icon: "question",
category: 30,
summary: "Is something missing in Funkwhale? Write a feature request and launch a discussion about it."
},
{
name: "Support other community members",
icon: "wrench",
category: 30,
summary: "Help other community members troubleshot and solve their issues while installing or using Funkwhale."
},
{
name: "Review submitted patches",
icon: "eye",
category: 30,
summary: "Review work submitted by other contributors to increase the software's quality."
},
{
name: "Write about Funkwhale",
icon: "pencil",
category: 60,
summary: "Do you own a blog or a site? Writing about Funkwhale is a great way to help the project grow and attract new users and contributors."
},
{
name: "Solve a small issue",
icon: "file-code-o",
category: 60,
summary: "Improve the project by fixing a reported bug or implementing a requested enhancement."
},
{
name: "Open a Funkwhale instance",
icon: "server",
category: 120,
summary: "Hosting your own Funkwhale and opening it to other people makes the network stronger and more attractive."
},
{
name: "Audit Funkwhale's security",
icon: "user-secret",
category: 120,
summary: "Investigate and report possible security issues, and help us solve them before they affect our users."
},
{
name: "Integrate Funkwhale with other projects",
icon: "file-code-o",
category: 9999,
summary: "Write an app, an alternative client for Funkwhale, or integrate Funkwhale with third-party projects."
},
]
};
},
created() {
this.fetchStats();
this.fetch();
},
methods: {
toggleModal(type) {
this.modals[type] = true;
},
getComponent(type) {
let mapping = {
"dev:issue": Issue,
donation: Donation
};
return mapping[type] || ContributionItem;
},
fetchStats() {
let self = this;
let url = "http://localhost:8000/api/contributions/stats/";
axios.get(url).then(response => {
self.stats = response.data;
});
},
fetch(append) {
let self = this;
let filters = {};
if (this.filters.type) {
filters.type = this.filters.type;
}
let promise;
if (append) {
promise = axios.get(this.next);
} else {
let url = "http://localhost:8000/api/contributions/";
promise = axios.get(url, { params: filters });
}
promise.then(response => {
if (append) {
self.contributions = self.contributions.concat(response.data.results);
} else {
self.contributions = response.data.results;
}
self.next = response.data.next;
});
}
},
computed: {
suggestedTasks () {
let self = this
let tasks = this.availableTasks.filter(e => {
if (!self.availableTime) {
return true
}
return e.category === self.availableTime
})
tasks.sort((a, b) => {
return a.category - b.category
})
return tasks
}
},
watch: {
filters: {
handler() {
this.fetch();
},
deep: true
}
}
};
</script>
<style>
<style lang="scss">
.container.text {
max-width: 40em;
}
.main.container {
margin: 1em auto;
}
#app {
padding-bottom: 5em;
}
.button.is-fullwidth {
margin-top: 1em;
}
hr.hidden {
visibility: hidden;
}
hr.small {
margin: 0.5em 0;
}
</style>
front/src/assets/logo.png

6.69 KB | W: | H:

front/src/assets/logo.png

129 Bytes | W: | H:

front/src/assets/logo.png
front/src/assets/logo.png
front/src/assets/logo.png
front/src/assets/logo.png
  • 2-up
  • Swipe
  • Onion skin
<template>
<i :class="['fa', 'fa-' + name]" aria-hidden="true"></i>
<i :class="['icon', 'fa', 'fa-' + name]" aria-hidden="true"></i>
</template>
<script>
......@@ -9,3 +9,8 @@ export default {
props: ['name']
}
</script>
<style lang="scss">
.icon.is-large {
font-size: 2em;
}
</style>
......@@ -9,7 +9,7 @@
<section class="modal-card-body">
<slot></slot>
</section>
<footer class="modal-card-foot">
<footer v-if="showFooter" class="modal-card-foot">
<button class="button is-success">Save changes</button>
<button class="button">Cancel</button>
</footer>
......@@ -18,6 +18,17 @@
</template>
<script>
export default {
props: ["show"]
props: {
show: {type: Boolean, default: false},
showFooter: {type: Boolean, default: false},
}
};
</script>
<style lang="scss" scoped>
.large.modal .modal-card {
width: 1000px;
}
.modal-card-title {
flex-shrink: initial;
}
</style>
......@@ -33,7 +33,11 @@
More than two hours
</template>
</small>
<button class="right floated button is-link">Get started</button>
<router-link
:to="{name: 'guide', params: {slug: task.slug}}"
v-if="task.slug && task.steps && task.steps.length > 0"
class="right floated button is-link">Get started</router-link>
</div>
</article>
</div>
......
import Vue from "vue";
import Icon from "./Icon";
import GuideMenu from "./guide/Menu";
import GuideContent from "./guide/Content";
import GuideStep from "./guide/Step";
import StepImage from "./guide/Image";
import Modal from "./Modal";
Vue.component('icon', Icon)
Vue.component('guide-menu', GuideMenu)
Vue.component('guide-content', GuideContent)
Vue.component('guide-step', GuideStep)
Vue.component('step-image', StepImage)
Vue.component('modal', Modal)
export default {}
<template>
<div class="container">
<h1 class="title">{{ metadata.name }}</h1>
<p>{{ metadata.summary }}</p>
<hr class="hidden">
<guide-step
v-for="(step, i) in metadata.steps"
:current="current"
:step="step"
:index="i + 1"></guide-step>
<hr class="hidden" />
<p class="success-message">
<icon name="check"/> You're done, thank you for the help!
</p>
</div>
</template>
<script>
export default {
props: ["metadata", "current"]
};
</script>
<style>
.success-message {
text-align: center;
font-size: 1.5em;
}
</style>
<template>
<figure class="image box">
<a href="" title="Click to zoom" @click.stop.prevent="modal = true">
<icon name="search" class="is-large"></icon>
<img :src="media.url" class="square" :alt="media.caption">
<!-- <div class="img" :style="{'background-image': 'url(' + media.url + ')'}">
</div> -->
<!-- <img :src="media.url" :alt="media.caption"> -->
</a>
<p>{{ media.caption }}</p>
<div class="modals">
<modal class="large" :show="modal" @close="modal = false">
<template slot="title">{{ media.caption }}</template>
<img :src="media.url" :alt="media.caption">
</modal>
</div>
</figure>
</template>
<script>
export default {
props: ['media'],
data () {
return {
modal: false,
}
}
}
</script>
<style lang="scss" scoped>
figure {
box-sizing: border-box;
padding: 0.5em;
text-align: center;
max-width: 13em;
background-color: rgba(20, 16, 16, 0.05);
&:not(:last-child) {
margin-right: 1em;
}
.square {
object-fit: fill;
margin: 0 auto;
}
> a {
display: block;
width: 100%;
height: 10em;
overflow: hidden;
.icon {
position: absolute;
top: 0.5em;
right: 0.5em;
}
}
}
</style>
<template>
<aside class="menu">
<ul class="menu-list">
<li>
<router-link to="/">
<icon name="arrow-left"></icon>
Return home
</router-link>
</li>
</ul>
<hr>
<div class="field">
<div class="label">
<icon name="clock-o"></icon>&nbsp;
Duration:
</div>
<div class="value">
<template v-if="metadata.category === 5">
5 minutes
</template>
<template v-else-if="metadata.category === 15">
15 minutes
</template>
<template v-else-if="metadata.category === 30">
30 minutes
</template>
<template v-else-if="metadata.category === 60">
One hour
</template>
<template v-else-if="metadata.category === 120">
2 hours
</template>
<template v-else-if="metadata.category === 9999">
More than two hours
</template>
</div>
</div>
<div class="field skills" v-if="metadata.skills && metadata.skills.length > 0 ">
<div class="label">
<icon name="wrench"></icon>&nbsp;
Required skills:
</div>
<div class="value">
<ul>
<li v-for="skill in metadata.skills" :title="skill.summary">
{{ skill.name }} <icon class="right floated" name="question-circle-o"></icon>
</li>
</ul>
</div>
</div>
<hr>
<p class="menu-label">Steps</p>
<ul class="menu-list">
<li v-for="(step, i) in metadata.steps">
<a
@click.prevent="$emit('step-changed', i + 1)"
:href="'#step-' + (i + 1)"
:class="[{'is-active': current === 'step-' + (i + 1)}]">
{{ i+1 }}. {{ step.title }}</a>
</li>
</ul>
<hr>
<p class="menu-label">Do you need some help?</p>
<ul class="menu-list">
<li>
<a rel="noopener noreferrer" href="https://matrix.to/#/!SwDTmjceJHHroSEIPP:matrix.org" target="_blank">
<icon name="matrix-org"></icon> Chat with us on #Funkwhale:Matrix.org
</a>
</li>
<li>
<a rel="noopener noreferrer" href="https://socialhub.network/c/funkwhale/support" target="_blank">
<icon name="comment"></icon> Ask your question on Socialhub.network
</a>
</li>
<li>
<a rel="noopener noreferrer" href="https://mastodon.eliotberriot.com/@funkwhale" target="_blank">
<icon name="mastodon"></icon> Drop us a toot on Mastodon
</a>
</li>
</ul>
<hr class="small hidden">
<p>Include a link to this page and the number of the step that's giving you trouble.</p>
</aside>
</template>
<script>
export default {
props: ["metadata", "current"]
};
</script>
<style lang="scss" scoped>
.field {
.label, .value {
display: inline;
}
&.skills ul {
margin: 1em;
}
}
</style>
<template>
<div :class="['box', {'is-active': current === 'step-' + index}]" :id="'step-' + index">
<h3 class="subtitle">
<span v-if="step.optional" class="tag is-info">Optional</span>
Step {{ index }}: {{ step.title }}
</h3>
<p>{{ step.content }}</p>
<p class="buttons" v-if="step.links && step.links.length > 0">
<template v-for="link in step.links">
<router-link class="button" :to="link.to" v-if="link.to">
<icon :name="link.icon" v-if="link.icon" />
<icon name="link" v-else/>&nbsp;
{{ link.text }}
</router-link>
<a class="button" v-else :href="link.url" rel="noopener noreferred" target="_blank">
<icon :name="link.icon" v-if="link.icon" />
<icon name="link" v-else/>&nbsp;
{{ link.text }}
</a>
</template>
</p>
<div class="media-wrapper" v-if="step.media && step.media.length > 0">
<hr>
<div class="media-obects">
<template v-for="media in step.media">
<step-image :media="media" v-if="media.type === 'image'"></step-image>
</template>
</div>
</div>
</div>
</template>
<script>
export default {
props: ["step", "index", "current"]
};
</script>
<style lang="scss" scoped>
.buttons {
margin-top: 1em;
}
.media-wrapper {
margin-top: 1em;
}
.media-obects {