Good working day

This commit is contained in:
sheychen 2019-04-15 18:01:30 +02:00
parent 3c92244f5e
commit 2f52372e35
21 changed files with 10316 additions and 174 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*node_modules
*vscode

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

10078
compiler/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,8 @@
"@babel/preset-env": "^7.2.0",
"autoprefixer": "^8.3.0",
"babel-loader": "^8.0.5",
"chart.js": "^2.8.0",
"core-js": "^2.6.5",
"css-loader": "^0.28.11",
"file-loader": "^1.1.11",
"friendly-errors-webpack-plugin": "^1.7.0",
@ -38,16 +40,17 @@
"less-loader": "^4.1.0",
"node-sass": "^4.11.0",
"optimize-css-assets-webpack-plugin": "^4.0.0",
"pug": "^2.0.3",
"pug-plain-loader": "^1.0.0",
"sass-loader": "^7.0.1",
"string-replace-webpack-plugin": "^0.1.3",
"url-loader": "^1.0.1",
"vue-chartjs": "^3.4.2",
"vue-loader": "^15.4.2",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.5.22",
"webpack": "^4.6.0",
"webpack-cli": "^3.2.1",
"pug": "^2.0.3",
"pug-plain-loader": "^1.0.0"
"webpack-cli": "^3.2.1"
},
"dependencies": {
"vue": "^2.5.22"

View File

@ -6,10 +6,15 @@ export const emitErrorMixin = {
}
}
export const saveOptionsMixin = {
export const handleOptionsMixin = {
methods: {
saveOptions(options) {
this.$emit("save", options)
},
setOption(name, value) {
const options = {...this.$props}
options[name] = value
this.saveOptions(options)
}
}
}

View File

@ -1,25 +0,0 @@
<template lang="pug">
div(@click="changeName()").
Hello, compiler.
</template>
<script>
export default {
name : 'example',
data() {
return {
name : 'world'
};
},
methods : {
changeName() {
this.name = 'foobar';
}
}
};
</script>
<style lang="sass" scoped>
div
font-weight: bold
</style>

View File

@ -1,6 +1,6 @@
<template lang="pug">
a.account(target="_blank" :href="account.url")
.avatar(:style="avatarStyle(account.avatar_static)")
.avatar(v-if="showMedia" :style="avatarStyle(account.avatar_static)")
.name(v-html="parseEmojis(account.display_name, account.emojis)")
.acct @{{ account.acct }}
</template>
@ -9,7 +9,13 @@ a.account(target="_blank" :href="account.url")
import { parseEmojisMixin } from './tools'
export default {
props: ["account"],
props: {
account: Object,
showMedia: {
type: Boolean,
default: true
}
},
mixins: [ parseEmojisMixin ],
methods: {
avatarStyle(avatar) {

View File

@ -2,21 +2,22 @@
.client
.statues
.header(v-if="notifications.length > 0") Accueil
.list(v-if="statues.length > 0")
.list(v-if="statues.length > 0" @scroll="onScroll")
template(v-for="status in statues")
status(v-if="showStatus(status)" :key="status.id" :status="status" :now="now" @mark="onStatusMark")
status(v-if="showStatus(status)" :key="status.id" :status="status" :now="now" :showMedia="showMedia" @mark="onStatusMark")
.status(v-show="loadingOlder") Loading...
template(v-else) Loading...
.notifications(v-if="notifications.length > 0")
.header
| Notifications
span.date(@click.stop.prevent="onNotificationsClear")
.list
notification(v-for="notification in notifications" :key="notification.id" :notification="notification" :now="now" @dismiss="onNotificationDismiss")
notification(v-for="notification in notifications" :key="notification.id" :notification="notification" :now="now" :showMedia="showMedia" @dismiss="onNotificationDismiss")
</template>
<script>
import { timerMinin } from '../core/fromNow.vue'
import { emitErrorMixin, saveOptionsMixin } from '../core/tools'
import { emitErrorMixin } from '../core/tools'
import statusVue from './status.vue'
import notificationVue from './notification.vue'
@ -33,7 +34,8 @@ export default {
reconnect: Boolean,
buffer: Number,
reblog: Boolean,
reply: Boolean
reply: Boolean,
showMedia: Boolean
},
data() {
return {
@ -46,17 +48,25 @@ export default {
}),
statues: [],
notifications: [],
now: Date.now()
now: Date.now(),
loadingOlder: false
};
},
methods: {
addStatus(status) {
this.statues.unshift(status)
this.statues.splice(this.buffer)
},
addNotification(notif) {
this.notifications.push(notif)
this.notifications.splice(this.buffer)
onScroll(event) {
if(!this.loadingOlder && event.target.scrollHeight - event.target.clientHeight - event.target.scrollTop - 100 < 0) {
this.loadingOlder = true
this.rest
.get("/timelines/home", { params: { limit: this.buffer,
max_id: this.statues[this.statues.length - 1].id } })
.then(res => {
this.statues.push.apply(this.statues, res.data)
this.loadingOlder = false
})
.catch(this.emitError)
} else if(event.target.scrollTop < 20) {
this.statues.splice(this.buffer)
}
},
removeStatus(id) {
for (var i = this.statues.length - 1; i >= 0; i--) {
@ -101,11 +111,11 @@ export default {
const payload = JSON.parse(event.payload)
switch (event.event) {
case "update":
this.addStatus(payload)
this.statues.unshift(payload)
break
case "notification":
this.addNotification(payload)
this.notifications.unshift(payload)
break
case "delete":

View File

@ -3,7 +3,7 @@
.header(@click="showSettings = !showSettings")
| Mastodon:
span(v-html="parseEmojis(account.display_name, account.emojis)")
| @{{ server }}
| {{ server ? '@' + server : '' }}
.settings(v-show="showSettings")
p
label(for="reconnect") Reconnect:
@ -17,6 +17,9 @@
p
label(for="buffer") Buffer:
input#buffer(type="number" :value="buffer" @keyup.enter="setOption('buffer', parseInt($event.target.value))")
p
label(for="showMedia") Show media:
input#showMedia(type="checkbox" :checked="showMedia" @change="setOption('showMedia', $event.target.checked)")
client(v-if="server && token" v-bind="$props")
.auth(v-else)
form(@submit.prevent="setServer")
@ -31,13 +34,13 @@
</template>
<script>
import { emitErrorMixin, saveOptionsMixin } from '../core/tools'
import { emitErrorMixin, handleOptionsMixin } from '../core/tools'
import { parseEmojisMixin } from './tools'
import clientVue from './client.vue'
export default { //TODO: Use oauth
name: 'mastodon',
mixins: [ emitErrorMixin, saveOptionsMixin, parseEmojisMixin ],
mixins: [ emitErrorMixin, handleOptionsMixin, parseEmojisMixin ],
components: {
client: clientVue
},
@ -63,6 +66,10 @@ export default { //TODO: Use oauth
reply: {
default: false,
type: Boolean
},
showMedia: {
default: true,
type: Boolean
}
},
data() {
@ -81,11 +88,6 @@ export default { //TODO: Use oauth
}).then(() => this.saveOptions({...this.$props,
server: this.newServer, token: this.newToken}))
.catch(this.emitError)
},
setOption(name, value) {
const options = {...this.$props}
options[name] = value
this.saveOptions(options)
}
},
created() {

View File

@ -1,6 +1,6 @@
<template lang="pug">
.notification
account(:account="notification.account")
account(:account="notification.account" :showMedia="showMedia")
span.colored.text-icon.letter(v-if="notification.type == 'mention'")
span.colored.text-icon.letter(v-if="notification.type == 'reblog'")
@ -11,7 +11,7 @@
.content
template(v-if="notification.type == 'follow'") Vous suit
status.reblog(v-else-if="notification.status" :status="notification.status" :now="now"
:withAccount="notification.type != 'mention'" @mark.stop.prevent="")
:showMedia="showMedia" :withAccount="notification.type != 'mention'" @mark.stop.prevent="")
a.date(@click.stop.prevent="makeDismiss" style="margin-top: -1em")
</template>
@ -22,7 +22,13 @@ import accountVue from './account.vue'
import statusVue from './status.vue'
export default {
props: ["notification"],
props: {
notification: Object,
showMedia: {
type: Boolean,
default: true
}
},
mixins: [ timedMixin ],
components: {
fromNow: fromNowVue,

View File

@ -1,7 +1,7 @@
<template lang="pug">
div
.status
account(v-if="withAccount" :account="status.account")
account(v-if="withAccount" :account="status.account" :showMedia="showMedia")
span.text-icon.letter(v-if="status.reblog")
@ -15,9 +15,11 @@ div
div(v-if="!status.spoiler_text || !status.sensitive")
.text(v-html="parseEmojis(status.content, status.emojis)")
a.media(v-for="media in status.media_attachments" :href="media.url" target="_blank")
img(v-if="media.type == 'image' || media.type == 'gifv'" :src="media.preview_url" :alt="media.description" :title="media.description")
.gif(v-if="media.type == 'gifv'") GIF
status.reblog(v-else :status="status.reblog" :now="now")
template(v-if="showMedia")
img(v-if="media.type == 'image' || media.type == 'gifv'" :src="media.preview_url" :alt="media.description" :title="media.description")
.gif(v-if="media.type == 'gifv'") GIF
template(v-else) Hidden media
status.reblog(v-else :status="status.reblog" :now="now" :showMedia="showMedia")
.meta(v-if="!status.reblog")
a.replies(@click.stop.prevent="makeReply(status)")
@ -47,6 +49,10 @@ export default {
},
props: {
status: Object,
showMedia: {
type: Boolean,
default: true
},
withAccount: {
type: Boolean,
default: true

View File

@ -31,12 +31,12 @@
</template>
<script>
import { emitErrorMixin, saveOptionsMixin } from '../core/tools'
import { emitErrorMixin, handleOptionsMixin } from '../core/tools'
import fromNowVue, { timerMinin } from '../core/fromNow.vue';
export default {
name: 'nextcloud-news',
mixins: [ emitErrorMixin, timerMinin, saveOptionsMixin ],
mixins: [ emitErrorMixin, timerMinin, handleOptionsMixin ],
components: {
fromNow: fromNowVue
},
@ -103,11 +103,6 @@ export default {
}).then(() => this.saveOptions({...this.$props,
server: this.newServer, token: this.newToken, username: this.newUsername }))
.catch(this.emitError)
},
setOption(name, value) {
const options = {...this.$props}
options[name] = value
this.saveOptions(options)
}
},
created() {

View File

@ -0,0 +1,72 @@
<script>
import { Bar, mixins } from 'vue-chartjs'
import moment from 'moment'
const { reactiveProp } = mixins
export default {
extends: Bar,
mixins: [ reactiveProp ],
mounted () {
this.renderChart(this.chartData, {
responsive: true,
legend: {
labels: {
fontColor: "white"
}
},
scales: {
xAxes: [{
type: 'time',
distribution: 'series',
ticks: {
fontColor: "white",
source: 'data',
autoSkip: true,
maxRotation: 0,
autoSkipPadding: 5
},
time: {
displayFormats: {
hour: 'HH[h]'
}
}
}],
yAxes: [{
id: 'y-axis-temp',
display: true,
position: 'left',
ticks: {
fontColor: "white"
}
},{
id: 'y-axis-rain',
display: true,
position: 'right',
ticks: {
fontColor: "white",
beginAtZero: true,
suggestedMax: 1
}
}]
},
tooltips: {
intersect: false,
callbacks: {
title: function(tooltipItem, myData) {
var item = myData.datasets[tooltipItem[0].datasetIndex].data[tooltipItem[0].index]
return moment(item.x || item.t).format('HH[h]')
},
label: function(tooltipItem, myData) {
var label = myData.datasets[tooltipItem.datasetIndex].label || '';
if (label) {
label += ': ';
}
label += tooltipItem.value;
return label;
}
}
}
})
}
}
</script>

View File

@ -8,6 +8,9 @@
p
label(for="update") Update interval:
input#update(type="number" :value="update" @keyup.enter="setOption('update', parseInt($event.target.value))")
p
label(for="forecastLimit") Forecast limit:
input#forecastLimit(type="number" :value="forecastLimit" @keyup.enter="setOption('forecastLimit', parseInt($event.target.value))")
p
button(@click="showAdd = true") Add city
template(v-if="weathers.length > 0 || cities.length == 0")
@ -24,27 +27,21 @@
| {{ city.main.temp }}°C {{ city.main.humidity }}%
input.weather(v-show="showAdd" placeholder="city id" @keyup.enter="addCity(parseInt($event.target.value))")
.forecast
template(v-if="forecast")
.list
.line(v-for="line in forecast")
| {{ formatDate(line.dt) }}
.data
| {{ line.main.temp }}°C {{ line.main.humidity }}%
.main(v-for="main in line.weather")
.ic
img(:src="`https://openweathermap.org/img/w/${main.icon}.png`" :alt="main.main")
p {{ main.description }}
chart(v-if="forecast" :chartData="forecastChart")
template(v-else) Loading...
template(v-else) Loading...
</template>
<script>
import { emitErrorMixin, saveOptionsMixin } from '../core/tools'
import { emitErrorMixin, handleOptionsMixin } from '../core/tools'
import chartVue from './chart.vue'
export default {
name: 'openweathermap',
components: {},
mixins: [ emitErrorMixin, saveOptionsMixin ],
components: {
chart: chartVue
},
mixins: [ emitErrorMixin, handleOptionsMixin ],
props: {
token: String,
cities: {
@ -64,6 +61,10 @@ export default {
lang: {
default: 'fr',
type: String
},
forecastLimit: {
default: 9,
type: Number
}
},
data() {
@ -82,6 +83,31 @@ export default {
showAdd: this.cities.length == 0
};
},
computed: {
forecastChart() { return {
datasets: [{
type: 'line',
label: 'Temperature',
yAxisID: 'y-axis-temp',
borderColor: 'white',
borderWidth: 1,
fill: false,
data: this.forecast.map(function (line) { return {
x: line.dt * 1000, y: line.main.temp
} })
},{
type: 'bar',
label: 'Percipitation',
yAxisID: 'y-axis-rain',
borderColor: '#DDDDDD',
backgroundColor: '#DDDDDD33',
borderWidth: 1,
data: this.forecast.filter(f => 'rain' in f && '3h' in f.rain).map(function (line) { return {
x: line.dt * 1000, y: line.rain['3h']
} })
}]
} }
},
methods: {
makeSelect(id) {
this.selected = id
@ -103,7 +129,8 @@ export default {
loadForecast() {
if(this.weathers[this.selected]) {
this.rest.get('forecast', { params: {
id: this.weathers[this.selected].id
id: this.weathers[this.selected].id,
cnt: this.forecastLimit
}})
.then(res => this.forecast = res.data.list)
.catch(this.emitError)
@ -117,11 +144,6 @@ export default {
const options = {...this.$props}
options.cities.push({id: id})
this.saveOptions(options)
},
setOption(name, value) {
const options = {...this.$props}
options[name] = value
this.saveOptions(options)
}
},
created() {

View File

@ -25,6 +25,10 @@ module.exports = (env) => {
entry : {
[filename] : './entry.js'
},
performance: {
maxEntrypointSize: 512000,
maxAssetSize: 512000
},
output : {
filename : '[name].js',
path : path.resolve(__dirname, 'dist', filepath)

View File

@ -1,6 +1,7 @@
* {
margin: 0;
padding: 0;
font-family: Verdana, Geneva, sans-serif;
}
body {
@ -62,14 +63,14 @@ a {
flex: 1;
}
.mastodon .header, .mastodon .settings, .mastodon .client .list > div {
#services > div > .header, #services > div > .settings {
margin: 0.3em;
background-color: #222;
border-radius: 0.3em;
padding: 0.3em;
}
.mastodon .header {
#services > div > .header {
font-size: large;
text-align: center;
font-weight: bold;
@ -91,6 +92,13 @@ a {
min-height: min-content;
}
.mastodon .client .list > div {
margin: 0.3em;
background-color: #222;
border-radius: 0.3em;
padding: 0.3em;
}
.mastodon .client .statues {
-webkit-box-flex: 1;
-ms-flex: 1;
@ -182,19 +190,6 @@ a {
max-width: 30%;
}
.openweathermap > .header, .openweathermap .settings {
margin: 0.3em;
background-color: #222;
border-radius: 0.3em;
padding: 0.3em;
}
.openweathermap > .header {
font-size: large;
text-align: center;
font-weight: bold;
}
.openweathermap .ic {
overflow: hidden;
height: 30px;
@ -227,35 +222,11 @@ a {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
max-height: 100%;
overflow-y: scroll;
overflow-y: auto;
overflow-x: hidden;
}
.openweathermap .forecast .line {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
min-width: 15em;
margin: 0.3em;
background-color: #222;
border-radius: 0.3em;
padding: 0.3em;
}
.openweathermap .forecast .line .data {
float: right;
}
.openweathermap .forecast .line .main p {
display: inline;
}
.openweathermap .weather {
min-width: 17em;
border: 1px solid #222;
@ -283,19 +254,6 @@ a {
vertical-align: top;
}
.nextcloud-news > .header, .nextcloud-news .settings {
margin: 0.3em;
background-color: #222;
border-radius: 0.3em;
padding: 0.3em;
}
.nextcloud-news > .header {
font-size: large;
text-align: center;
font-weight: bold;
}
.nextcloud-news .news {
margin: 0.3em;
background-color: #222;

9
main.css.map Normal file

File diff suppressed because one or more lines are too long

View File

@ -40,11 +40,11 @@ var app = new Vue({
this.saveServices()
},
setService(id, options) {
this.services.push({
this.$set(this.services, id, {
type: this.services[id].type,
options: options
})
this.removeService(id)
this.saveServices()
},
removeService(id) {
this.services.splice(id, 1)
@ -52,6 +52,7 @@ var app = new Vue({
},
saveServices() {
localStorage.setItem(servicesStorage, JSON.stringify(this.services))
this.$forceUpdate()
}
}
})

View File

@ -16,6 +16,7 @@ $foreColor: #eee
*
margin: 0
padding: 0
font-family: Verdana, Geneva, sans-serif
body
background-color: $backColor
@ -55,14 +56,14 @@ a
display: flex
& > div
flex: 1
& > .header, & > .settings
@include tile
& > .header
font-size: large
text-align: center
font-weight: bold
.mastodon
.header, .settings, .client .list > div
@include tile
.header
font-size: large
text-align: center
font-weight: bold
.client
display: flex
height: 100vh
@ -71,6 +72,8 @@ a
height: 100%
overflow-y: auto
min-height: min-content
& > div
@include tile
.statues
flex: 1
.notifications
@ -125,12 +128,6 @@ a
float: right
.openweathermap
& > .header, .settings
@include tile
& > .header
font-size: large
text-align: center
font-weight: bold
.ic
overflow: hidden
height: 30px
@ -149,19 +146,9 @@ a
@include tile
.forecast
flex: 1
display: flex
flex-wrap: wrap
max-height: 100%
overflow-y: scroll
overflow-y: auto
overflow-x: hidden
.line
flex: 1
min-width: 15em
@include tile
.data
float: right
.main p
display: inline
.weather
min-width: 17em
border: 1px solid $tileColor
@ -179,12 +166,6 @@ a
vertical-align: top
.nextcloud-news
& > .header, .settings
@include tile
& > .header
font-size: large
text-align: center
font-weight: bold
.news
@include tile
.date