Update 19.11.2022
97
HTML/gcphone/src/App.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div style="height: 100vh; width: 100vw;" @contextmenu="closePhone">
|
||||
<notification />
|
||||
<div v-if="show === true && tempoHide === false" :style="{zoom: zoom}" @contextmenu.stop>
|
||||
<div class="phone_wrapper">
|
||||
<div v-if="coque" class="phone_coque" :style="{backgroundImage: 'url(/html/static/img/coque/' + coque.value + ')'}"></div>
|
||||
|
||||
<div id="app" class="phone_screen">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import './PhoneBaseStyle.scss'
|
||||
import './assets/css/font-awesome.min.css'
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
export default {
|
||||
name: 'app',
|
||||
components: {
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
soundCall: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['loadConfig', 'rejectCall']),
|
||||
closePhone () {
|
||||
this.$phoneAPI.closePhone()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['show', 'zoom', 'coque', 'sonido', 'appelsInfo', 'myPhoneNumber', 'volume', 'tempoHide'])
|
||||
},
|
||||
watch: {
|
||||
appelsInfo (newValue, oldValue) {
|
||||
if (this.appelsInfo !== null && this.appelsInfo.is_accepts !== true) {
|
||||
if (this.soundCall !== null) {
|
||||
this.soundCall.pause()
|
||||
}
|
||||
if (this.appelsInfo.initiator === true) {
|
||||
this.soundCall = new Audio('/html/static/sound/Phone_Call_Sound_Effect.ogg')
|
||||
} else {
|
||||
this.soundCall = new Audio('/html/static/sound/' + this.sonido.value)
|
||||
}
|
||||
this.soundCall.loop = true
|
||||
this.soundCall.volume = this.volume
|
||||
this.soundCall.play()
|
||||
} else if (this.soundCall !== null) {
|
||||
this.soundCall.pause()
|
||||
this.soundCall = null
|
||||
}
|
||||
if (newValue === null && oldValue !== null) {
|
||||
this.$router.push({name: 'home'})
|
||||
return
|
||||
}
|
||||
if (newValue !== null) {
|
||||
this.$router.push({name: 'appels.active'})
|
||||
}
|
||||
},
|
||||
show () {
|
||||
if (this.appelsInfo !== null) {
|
||||
this.$router.push({name: 'appels.active'})
|
||||
} else {
|
||||
this.$router.push({name: 'home'})
|
||||
}
|
||||
if (this.show === false && this.appelsInfo !== null) {
|
||||
this.rejectCall()
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.loadConfig()
|
||||
window.addEventListener('message', (event) => {
|
||||
if (event.data.keyUp !== undefined) {
|
||||
this.$bus.$emit('keyUp' + event.data.keyUp)
|
||||
}
|
||||
})
|
||||
window.addEventListener('keyup', (event) => {
|
||||
const keyValid = ['ArrowRight', 'ArrowLeft', 'ArrowUp', 'ArrowDown', 'Backspace', 'Enter']
|
||||
if (keyValid.indexOf(event.key) !== -1) {
|
||||
this.$bus.$emit('keyUp' + event.key)
|
||||
}
|
||||
if (event.key === 'Escape') {
|
||||
this.$phoneAPI.closePhone()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
76
HTML/gcphone/src/Notification/Notification.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div class="notifications">
|
||||
<div
|
||||
v-for='notif in list'
|
||||
:key="notif.id"
|
||||
class="notification"
|
||||
:style="style(notif)"
|
||||
>
|
||||
<div class="title">
|
||||
<i v-if="notif.icon" class="fa" :class="'fa-' + notif.icon"/> {{notif.title}}
|
||||
</div>
|
||||
<div class="message">{{notif.message}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import events from './events'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
currentId: 0,
|
||||
list: []
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
events.$on('add', this.addItem)
|
||||
},
|
||||
methods: {
|
||||
async addItem (event = {}) {
|
||||
const dataNotif = {
|
||||
...event,
|
||||
id: this.currentId ++,
|
||||
duration: parseInt(event.duration) || 3000
|
||||
}
|
||||
this.list.push(dataNotif)
|
||||
window.setTimeout(() => {
|
||||
this.destroy(dataNotif.id)
|
||||
}, dataNotif.duration)
|
||||
if (event.sound !== null && event.sound !== undefined) {
|
||||
const audio = new Audio('/html/static/sound/' + event.sound)
|
||||
audio.addEventListener('ended', () => {
|
||||
audio.src = null
|
||||
})
|
||||
audio.play()
|
||||
}
|
||||
},
|
||||
style (notif) {
|
||||
return {
|
||||
backgroundColor: notif.backgroundColor
|
||||
}
|
||||
},
|
||||
destroy (id) {
|
||||
this.list = this.list.filter(n => n.id !== id)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.notification {
|
||||
width: 450px;
|
||||
background-color: rgba(29, 161, 242, 0.6);
|
||||
color: white;
|
||||
padding: 8px 16px;
|
||||
margin-bottom: 8px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.title {
|
||||
font-size: 18px;
|
||||
}
|
||||
.message {
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
||||
2
HTML/gcphone/src/Notification/events.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import Vue from 'vue'
|
||||
export default new Vue()
|
||||
24
HTML/gcphone/src/Notification/index.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import Vue from 'vue'
|
||||
import Notification from './Notification'
|
||||
import events from './events'
|
||||
|
||||
const Notify = {
|
||||
install: function (options) {
|
||||
if (this.installed) return
|
||||
this.installed = true
|
||||
|
||||
Vue.component('notification', Notification)
|
||||
const notify = (params) => {
|
||||
events.$emit('add', params)
|
||||
}
|
||||
|
||||
Vue.notify = notify
|
||||
Object.defineProperties(Vue.prototype, {
|
||||
$notify: {
|
||||
get: () => Vue.notify
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default Notify
|
||||
359
HTML/gcphone/src/PhoneAPI.js
Normal file
@@ -0,0 +1,359 @@
|
||||
import store from '@/store'
|
||||
import VoiceRTC from './VoiceRCT'
|
||||
import Vue from 'vue'
|
||||
|
||||
import emoji from './emoji.json'
|
||||
const keyEmoji = Object.keys(emoji)
|
||||
|
||||
let USE_VOICE_RTC = false
|
||||
const BASE_URL = 'http://gcphone/'
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
class PhoneAPI {
|
||||
constructor () {
|
||||
window.addEventListener('message', (event) => {
|
||||
const eventType = event.data.event
|
||||
if (eventType !== undefined && typeof this['on' + eventType] === 'function') {
|
||||
this['on' + eventType](event.data)
|
||||
} else if (event.data.show !== undefined) {
|
||||
store.commit('SET_PHONE_VISIBILITY', event.data.show)
|
||||
}
|
||||
})
|
||||
this.config = null
|
||||
this.voiceRTC = null
|
||||
this.soundList = {}
|
||||
}
|
||||
|
||||
async post (method, data) {
|
||||
const ndata = data === undefined ? '{}' : JSON.stringify(data)
|
||||
const response = await window.jQuery.post(BASE_URL + method, ndata)
|
||||
return JSON.parse(response)
|
||||
}
|
||||
|
||||
async log (...data) {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
return this.post('log', data)
|
||||
} else {
|
||||
return console.log(...data)
|
||||
}
|
||||
}
|
||||
|
||||
convertEmoji (text) {
|
||||
for (const e of keyEmoji) {
|
||||
text = text.replace(new RegExp(`:${e}:`, 'g'), emoji[e])
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
// === Gestion des messages
|
||||
async sendMessage (phoneNumber, message) {
|
||||
return this.post('sendMessage', {phoneNumber, message})
|
||||
}
|
||||
async deleteMessage (id) {
|
||||
return this.post('deleteMessage', {id})
|
||||
}
|
||||
async deleteMessagesNumber (number) {
|
||||
return this.post('deleteMessageNumber', {number})
|
||||
}
|
||||
async deleteAllMessages () {
|
||||
return this.post('deleteAllMessage')
|
||||
}
|
||||
async setMessageRead (number) {
|
||||
return this.post('setReadMessageNumber', {number})
|
||||
}
|
||||
|
||||
// === Gestion des contacts
|
||||
async updateContact (id, display, phoneNumber) {
|
||||
return this.post('updateContact', { id, display, phoneNumber })
|
||||
}
|
||||
async addContact (display, phoneNumber) {
|
||||
return this.post('addContact', { display, phoneNumber })
|
||||
}
|
||||
async deleteContact (id) {
|
||||
return this.post('deleteContact', { id })
|
||||
}
|
||||
|
||||
// == Gestion des appels
|
||||
async appelsDeleteHistorique (numero) {
|
||||
return this.post('appelsDeleteHistorique', { numero })
|
||||
}
|
||||
async appelsDeleteAllHistorique () {
|
||||
return this.post('appelsDeleteAllHistorique')
|
||||
}
|
||||
|
||||
// === Autre
|
||||
async closePhone () {
|
||||
return this.post('closePhone')
|
||||
}
|
||||
async setUseMouse (useMouse) {
|
||||
return this.post('useMouse', useMouse)
|
||||
}
|
||||
async setGPS (x, y) {
|
||||
return this.post('setGPS', {x, y})
|
||||
}
|
||||
async takePhoto () {
|
||||
store.commit('SET_TEMPO_HIDE', true)
|
||||
const data = await this.post('takePhoto', { url: this.config.fileUploadService_Url, field: this.config.fileUploadService_Field })
|
||||
store.commit('SET_TEMPO_HIDE', false)
|
||||
return data
|
||||
}
|
||||
async getReponseText (data) {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
return this.post('reponseText', data || {})
|
||||
} else {
|
||||
return {text: window.prompt()}
|
||||
}
|
||||
}
|
||||
|
||||
async faketakePhoto () {
|
||||
return this.post('faketakePhoto')
|
||||
}
|
||||
|
||||
async callEvent (eventName, data) {
|
||||
return this.post('callEvent', {eventName, data})
|
||||
}
|
||||
async deleteALL () {
|
||||
localStorage.clear()
|
||||
store.dispatch('tchatReset')
|
||||
store.dispatch('notesReset')
|
||||
store.dispatch('resetPhone')
|
||||
store.dispatch('resetMessage')
|
||||
store.dispatch('resetContact')
|
||||
store.dispatch('resetBourse')
|
||||
store.dispatch('resetAppels')
|
||||
return this.post('deleteALL')
|
||||
}
|
||||
async getConfig () {
|
||||
if (this.config === null) {
|
||||
const response = await window.jQuery.get('/html/static/config/config.json')
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
this.config = JSON.parse(response)
|
||||
} else {
|
||||
this.config = response
|
||||
}
|
||||
if (this.config.useWebRTCVocal === true) {
|
||||
this.voiceRTC = new VoiceRTC(this.config.RTCConfig)
|
||||
USE_VOICE_RTC = true
|
||||
}
|
||||
// console.log('JS USE RTC', this.config.useWebRTCVocal)
|
||||
this.notififyUseRTC(this.config.useWebRTCVocal)
|
||||
}
|
||||
return this.config
|
||||
}
|
||||
|
||||
async onsetEnableApp (data) {
|
||||
store.dispatch('setEnableApp', data)
|
||||
}
|
||||
|
||||
async setIgnoreFocus (ignoreFocus) {
|
||||
this.post('setIgnoreFocus', { ignoreFocus })
|
||||
}
|
||||
|
||||
// === App Tchat
|
||||
async tchatGetMessagesChannel (channel) {
|
||||
this.post('tchat_getChannel', { channel })
|
||||
}
|
||||
async tchatSendMessage (channel, message) {
|
||||
this.post('tchat_addMessage', { channel, message })
|
||||
}
|
||||
|
||||
// === App Notes
|
||||
async notesGetMessagesChannel (channel) {
|
||||
window.localStorage.setItem('gc_notas_locales', channel)
|
||||
}
|
||||
async notesSendMessage (channel, message) {
|
||||
this.post('notes_addMessage', { channel, message })
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Gestion des events
|
||||
// ==========================================================================
|
||||
onupdateMyPhoneNumber (data) {
|
||||
store.commit('SET_MY_PHONE_NUMBER', data.myPhoneNumber)
|
||||
}
|
||||
onupdateMessages (data) {
|
||||
store.commit('SET_MESSAGES', data.messages)
|
||||
}
|
||||
onnewMessage (data) {
|
||||
store.commit('ADD_MESSAGE', data.message)
|
||||
}
|
||||
onupdateContacts (data) {
|
||||
store.commit('SET_CONTACTS', data.contacts)
|
||||
}
|
||||
onhistoriqueCall (data) {
|
||||
store.commit('SET_APPELS_HISTORIQUE', data.historique)
|
||||
}
|
||||
onupdateBankbalance (data) {
|
||||
store.commit('SET_BANK_AMONT', data.banking)
|
||||
}
|
||||
onupdateBourse (data) {
|
||||
store.commit('SET_BOURSE_INFO', data.bourse)
|
||||
}
|
||||
// Call
|
||||
async startCall (numero, extraData = undefined) {
|
||||
if (USE_VOICE_RTC === true) {
|
||||
const rtcOffer = await this.voiceRTC.prepareCall()
|
||||
return this.post('startCall', { numero, rtcOffer, extraData })
|
||||
} else {
|
||||
return this.post('startCall', { numero, extraData })
|
||||
}
|
||||
}
|
||||
async acceptCall (infoCall) {
|
||||
if (USE_VOICE_RTC === true) {
|
||||
const rtcAnswer = await this.voiceRTC.acceptCall(infoCall)
|
||||
return this.post('acceptCall', { infoCall, rtcAnswer })
|
||||
} else {
|
||||
return this.post('acceptCall', { infoCall })
|
||||
}
|
||||
}
|
||||
async rejectCall (infoCall) {
|
||||
return this.post('rejectCall', { infoCall })
|
||||
}
|
||||
|
||||
async notififyUseRTC (use) {
|
||||
return this.post('notififyUseRTC', use)
|
||||
}
|
||||
|
||||
onwaitingCall (data) {
|
||||
store.commit('SET_APPELS_INFO_IF_EMPTY', {
|
||||
...data.infoCall,
|
||||
initiator: data.initiator
|
||||
})
|
||||
}
|
||||
onacceptCall (data) {
|
||||
if (USE_VOICE_RTC === true) {
|
||||
if (data.initiator === true) {
|
||||
this.voiceRTC.onReceiveAnswer(data.infoCall.rtcAnswer)
|
||||
}
|
||||
this.voiceRTC.addEventListener('onCandidate', (candidates) => {
|
||||
this.post('onCandidates', { id: data.infoCall.id, candidates })
|
||||
})
|
||||
}
|
||||
store.commit('SET_APPELS_INFO_IS_ACCEPTS', true)
|
||||
}
|
||||
oncandidatesAvailable (data) {
|
||||
this.voiceRTC.addIceCandidates(data.candidates)
|
||||
}
|
||||
onrejectCall (data) {
|
||||
if (this.voiceRTC !== null) {
|
||||
this.voiceRTC.close()
|
||||
}
|
||||
store.commit('SET_APPELS_INFO', null)
|
||||
}
|
||||
// Tchat Event
|
||||
ontchat_receive (data) {
|
||||
store.dispatch('tchatAddMessage', data)
|
||||
}
|
||||
ontchat_channel (data) {
|
||||
store.commit('TCHAT_SET_MESSAGES', data)
|
||||
}
|
||||
|
||||
// Notes Event
|
||||
onnotes_receive (data) {
|
||||
store.dispatch('notesAddMessage', data)
|
||||
}
|
||||
onnotes_channel (data) {
|
||||
store.commit('NOTES_SET_MESSAGES', data)
|
||||
}
|
||||
|
||||
// =====================
|
||||
onautoStartCall (data) {
|
||||
this.startCall(data.number, data.extraData)
|
||||
}
|
||||
onautoAcceptCall (data) {
|
||||
store.commit('SET_APPELS_INFO', data.infoCall)
|
||||
this.acceptCall(data.infoCall)
|
||||
}
|
||||
|
||||
// === Twitter
|
||||
twitter_login (username, password) {
|
||||
this.post('twitter_login', {username, password})
|
||||
}
|
||||
twitter_changePassword (username, password, newPassword) {
|
||||
this.post('twitter_changePassword', {username, password, newPassword})
|
||||
}
|
||||
twitter_createAccount (username, password, avatarUrl) {
|
||||
this.post('twitter_createAccount', {username, password, avatarUrl})
|
||||
}
|
||||
twitter_postTweet (username, password, message) {
|
||||
this.post('twitter_postTweet', { username, password, message })
|
||||
}
|
||||
twitter_postTweetImg (username, password, img) {
|
||||
this.post('twitter_postTweetImg', { username, password, img })
|
||||
}
|
||||
twitter_toggleLikeTweet (username, password, tweetId) {
|
||||
this.post('twitter_toggleLikeTweet', { username, password, tweetId })
|
||||
}
|
||||
twitter_setAvatar (username, password, avatarUrl) {
|
||||
this.post('twitter_setAvatarUrl', { username, password, avatarUrl })
|
||||
}
|
||||
twitter_getTweets (username, password) {
|
||||
this.post('twitter_getTweets', { username, password })
|
||||
}
|
||||
twitter_getFavoriteTweets (username, password) {
|
||||
this.post('twitter_getFavoriteTweets', { username, password })
|
||||
}
|
||||
ontwitter_tweets (data) {
|
||||
store.commit('SET_TWEETS', data)
|
||||
}
|
||||
ontwitter_favoritetweets (data) {
|
||||
store.commit('SET_FAVORITE_TWEETS', data)
|
||||
}
|
||||
ontwitter_newTweet (data) {
|
||||
store.dispatch('addTweet', data.tweet)
|
||||
}
|
||||
ontwitter_setAccount (data) {
|
||||
store.dispatch('setAccount', data)
|
||||
}
|
||||
ontwitter_updateTweetLikes (data) {
|
||||
store.commit('UPDATE_TWEET_LIKE', data)
|
||||
}
|
||||
ontwitter_setTweetLikes (data) {
|
||||
store.commit('UPDATE_TWEET_ISLIKE', data)
|
||||
}
|
||||
ontwitter_showError (data) {
|
||||
Vue.notify({
|
||||
title: store.getters.IntlString(data.title, ''),
|
||||
message: store.getters.IntlString(data.message),
|
||||
icon: 'twitter',
|
||||
backgroundColor: '#e0245e80'
|
||||
})
|
||||
}
|
||||
ontwitter_showSuccess (data) {
|
||||
Vue.notify({
|
||||
title: store.getters.IntlString(data.title, ''),
|
||||
message: store.getters.IntlString(data.message),
|
||||
icon: 'twitter'
|
||||
})
|
||||
}
|
||||
|
||||
onplaySound ({ sound, volume = 1 }) {
|
||||
if (!sound) return
|
||||
if (this.soundList[sound] !== undefined) {
|
||||
this.soundList[sound].volume = volume
|
||||
} else {
|
||||
this.soundList[sound] = new Audio('/html/static/sound/' + sound)
|
||||
this.soundList[sound].loop = true
|
||||
this.soundList[sound].volume = volume
|
||||
this.soundList[sound].play()
|
||||
}
|
||||
}
|
||||
|
||||
onsetSoundVolume ({ sound, volume = 1 }) {
|
||||
if (this.soundList[sound] !== undefined) {
|
||||
this.soundList[sound].volume = volume
|
||||
}
|
||||
}
|
||||
|
||||
onstopSound ({ sound }) {
|
||||
if (this.soundList[sound] !== undefined) {
|
||||
this.soundList[sound].pause()
|
||||
delete this.soundList[sound]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const instance = new PhoneAPI()
|
||||
|
||||
export default instance
|
||||
114
HTML/gcphone/src/PhoneBaseStyle.scss
Normal file
@@ -0,0 +1,114 @@
|
||||
body {
|
||||
font-size: 20px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.phone_infoBare {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.phone_app{
|
||||
height: 739px;
|
||||
width: 334px;
|
||||
top: 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.phone_title {
|
||||
font-weight: 300;
|
||||
padding-left: 24px;
|
||||
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
||||
margin-top: 4px;
|
||||
color: black;
|
||||
background-color: #e9e9eb;
|
||||
height: 64px;
|
||||
line-height: 52px;
|
||||
font-size: 24px;
|
||||
padding-left: 64px;
|
||||
}
|
||||
.phone_title .btn-back {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
color: #5A5A5A;
|
||||
line-height: 42px;
|
||||
border-radius: 50%;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
top: 35px;
|
||||
|
||||
outline: none;
|
||||
text-shadow: none;
|
||||
text-align: center;
|
||||
}
|
||||
.phone_title .btn-back:hover {
|
||||
background-color: rgba(255,255,255, 0.3);
|
||||
color: #5A5A5A;
|
||||
}
|
||||
|
||||
.phone_content {
|
||||
height: 100%;
|
||||
width: 104%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.phone_wrapper {
|
||||
position: absolute;
|
||||
bottom: 0vh;
|
||||
right: 0vh;
|
||||
width: 500px;
|
||||
height: 1000px;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.phone_coque{
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
width: 394px;
|
||||
height: 800px;
|
||||
top: 71px;
|
||||
left: 15px;
|
||||
pointer-events:none;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.phone_screen{
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
width: 326px;
|
||||
height: 742px;
|
||||
bottom: 100px;
|
||||
left: 50px;
|
||||
right: 50px;
|
||||
top: 100px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
||||
box-sizing: border-box;
|
||||
font-weight: 100;
|
||||
font-size: 19px;
|
||||
user-select: none;
|
||||
}
|
||||
*::-webkit-scrollbar-track
|
||||
{
|
||||
box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
*::-webkit-scrollbar
|
||||
{
|
||||
width: 3px;
|
||||
background-color: transparent;
|
||||
}
|
||||
*::-webkit-scrollbar-thumb
|
||||
{
|
||||
background-color: #666;
|
||||
}
|
||||
177
HTML/gcphone/src/TimeAgo.js
Normal file
@@ -0,0 +1,177 @@
|
||||
// Original: https://github.com/egoist/vue-timeago
|
||||
/* eslint-disable */
|
||||
const MINUTE = 60
|
||||
const HOUR = MINUTE * 60
|
||||
const DAY = HOUR * 24
|
||||
const WEEK = DAY * 7
|
||||
const MONTH = DAY * 30
|
||||
const YEAR = DAY * 365
|
||||
|
||||
function pluralOrSingular(data, locale) {
|
||||
if (data === 'just now') {
|
||||
return locale
|
||||
}
|
||||
const count = Math.round(data)
|
||||
if (Array.isArray(locale)) {
|
||||
return count > 1
|
||||
? locale[1].replace(/%s/, count)
|
||||
: locale[0].replace(/%s/, count)
|
||||
}
|
||||
return locale.replace(/%s/, count)
|
||||
}
|
||||
|
||||
function formatTime(time) {
|
||||
const d = new Date(time)
|
||||
return d.toLocaleString()
|
||||
}
|
||||
|
||||
const defaultLocales = {
|
||||
'de_DE': [
|
||||
"Gerade eben",
|
||||
["il y a %s seconde", "vor %s Sekunden"],
|
||||
["il y a %s minute", "vor %s Minuten"],
|
||||
["il y a %s heure", "vor %s Stunden"],
|
||||
["il y a %s jour", "vor %s Tagen"],
|
||||
["il y a %s semaine", "vor %s Wochen"],
|
||||
["il y a %s mois", "vor %s Monate"],
|
||||
["il y a %s an", "vor %s Jahre"]
|
||||
]
|
||||
}
|
||||
export default function install(Vue, { name = 'timeago', locale = 'de_DE', locales = defaultLocales} = {} ) {
|
||||
|
||||
if (!locales || Object.keys(locales).length === 0) {
|
||||
throw new TypeError('Expected locales to have at least one locale.')
|
||||
}
|
||||
const VueTimeago = {
|
||||
props: {
|
||||
since: {
|
||||
required: true
|
||||
},
|
||||
locale: String,
|
||||
maxTime: Number,
|
||||
autoUpdate: Number,
|
||||
format: Function
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
now: new Date().getTime()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentLocale() {
|
||||
if (Vue.prototype.$timeago) {
|
||||
const locale = VueTimeago.locales[VueTimeago.locale]
|
||||
if (locale) {
|
||||
return locale
|
||||
}
|
||||
}
|
||||
return locales['fr_FR']
|
||||
},
|
||||
sinceTime() {
|
||||
return new Date(this.since).getTime()
|
||||
},
|
||||
timeForTitle() {
|
||||
const seconds = this.now / 1000 - this.sinceTime / 1000
|
||||
|
||||
if (this.maxTime && seconds > this.maxTime) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.format
|
||||
? this.format(this.sinceTime)
|
||||
: formatTime(this.sinceTime)
|
||||
},
|
||||
timeago() {
|
||||
const seconds = this.now / 1000 - this.sinceTime / 1000
|
||||
|
||||
if (this.maxTime && seconds > this.maxTime) {
|
||||
clearInterval(this.interval)
|
||||
return this.format
|
||||
? this.format(this.sinceTime)
|
||||
: formatTime(this.sinceTime)
|
||||
}
|
||||
|
||||
const ret =
|
||||
seconds <= 5
|
||||
? pluralOrSingular('just now', this.currentLocale[0])
|
||||
: seconds < MINUTE
|
||||
? pluralOrSingular(seconds, this.currentLocale[1])
|
||||
: seconds < HOUR
|
||||
? pluralOrSingular(seconds / MINUTE, this.currentLocale[2])
|
||||
: seconds < DAY
|
||||
? pluralOrSingular(seconds / HOUR, this.currentLocale[3])
|
||||
: seconds < WEEK
|
||||
? pluralOrSingular(seconds / DAY, this.currentLocale[4])
|
||||
: seconds < MONTH
|
||||
? pluralOrSingular(seconds / WEEK, this.currentLocale[5])
|
||||
: seconds < YEAR
|
||||
? pluralOrSingular(
|
||||
seconds / MONTH,
|
||||
this.currentLocale[6]
|
||||
)
|
||||
: pluralOrSingular(
|
||||
seconds / YEAR,
|
||||
this.currentLocale[7]
|
||||
)
|
||||
|
||||
return ret
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.autoUpdate) {
|
||||
this.update()
|
||||
}
|
||||
},
|
||||
render(h) {
|
||||
return h(
|
||||
'time',
|
||||
{
|
||||
attrs: {
|
||||
datetime: new Date(this.since),
|
||||
title: this.timeForTitle
|
||||
}
|
||||
},
|
||||
this.timeago
|
||||
)
|
||||
},
|
||||
watch: {
|
||||
autoUpdate(newAutoUpdate) {
|
||||
this.stopUpdate()
|
||||
// only update when it's not falsy value
|
||||
// which means you cans set it to 0 to disable auto-update
|
||||
if (newAutoUpdate) {
|
||||
this.update()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
update() {
|
||||
const period = this.autoUpdate * 1000
|
||||
this.interval = setInterval(() => {
|
||||
this.now = new Date().getTime()
|
||||
}, period)
|
||||
},
|
||||
stopUpdate() {
|
||||
clearInterval(this.interval)
|
||||
this.interval = null
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.stopUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
VueTimeago.locale = 'fr_FR'
|
||||
VueTimeago.locales = {}
|
||||
|
||||
Vue.prototype.$timeago = {
|
||||
setCurrentLocale (locale) {
|
||||
VueTimeago.locale = locale
|
||||
},
|
||||
addLocale (locale, data) {
|
||||
VueTimeago.locales[locale] = data
|
||||
}
|
||||
}
|
||||
|
||||
Vue.component(name, VueTimeago)
|
||||
}
|
||||
51
HTML/gcphone/src/Utils.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import store from '@/store'
|
||||
|
||||
function getRGB (colorStr) {
|
||||
let match = colorStr.match(/rgba?\((\d{1,3}), ?(\d{1,3}), ?(\d{1,3})\)?(?:, ?(\d(?:\.\d?))\))?/)
|
||||
if (match !== null) {
|
||||
return {
|
||||
red: parseInt(match[1], 10),
|
||||
green: parseInt(match[2], 10),
|
||||
blue: parseInt(match[3], 10)
|
||||
}
|
||||
}
|
||||
match = colorStr.match(/^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})/)
|
||||
if (match !== null) {
|
||||
return {
|
||||
red: parseInt(match[1], 16),
|
||||
green: parseInt(match[2], 16),
|
||||
blue: parseInt(match[3], 16)
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function groupBy (xs, key) {
|
||||
return xs.reduce(function (rv, x) {
|
||||
(rv[x[key]] = rv[x[key]] || []).push(x)
|
||||
return rv
|
||||
}, {})
|
||||
}
|
||||
|
||||
export function generateColorForStr (str) {
|
||||
if (str.length === 0 || str[0] === '#') {
|
||||
return '#D32F2F'
|
||||
}
|
||||
const h = str.split('').reduce((prevHash, currVal) =>
|
||||
(((prevHash << 5) - prevHash) + currVal.charCodeAt(0)) | 0
|
||||
, 0)
|
||||
return store.getters.colors[Math.abs(h) % store.getters.colors.length]
|
||||
}
|
||||
|
||||
export function getBestFontColor (color) {
|
||||
const rgb = getRGB(color)
|
||||
if (rgb === undefined) {
|
||||
return '#000000'
|
||||
} else {
|
||||
if (rgb.red * 0.299 + rgb.green * 0.587 + rgb.blue * 0.114 > 186) {
|
||||
return 'rgba(0, 0, 0, 0.87)'
|
||||
} else {
|
||||
return '#FFFFFF'
|
||||
}
|
||||
}
|
||||
}
|
||||
128
HTML/gcphone/src/VoiceRCT.js
Normal file
@@ -0,0 +1,128 @@
|
||||
const constraints = {
|
||||
video: false,
|
||||
audio: true
|
||||
}
|
||||
|
||||
/* eslint-disable */
|
||||
class VoiceRTC {
|
||||
|
||||
constructor (RTCConfig) {
|
||||
this.myPeerConnection = null
|
||||
this.candidates = []
|
||||
this.listener = {}
|
||||
this.myCandidates = []
|
||||
this.audio = new Audio()
|
||||
this.offer = null
|
||||
this.answer = null
|
||||
this.initiator = null
|
||||
this.RTCConfig = RTCConfig
|
||||
}
|
||||
|
||||
async init () {
|
||||
await this.close()
|
||||
this.myPeerConnection = new RTCPeerConnection(this.RTCConfig)
|
||||
this.stream = await navigator.mediaDevices.getUserMedia(constraints)
|
||||
}
|
||||
|
||||
newConnection () {
|
||||
this.close()
|
||||
this.candidates = []
|
||||
this.myCandidates = []
|
||||
this.listener = {}
|
||||
this.offer = null
|
||||
this.answer = null
|
||||
this.initiator = null
|
||||
this.myPeerConnection = new RTCPeerConnection(this.RTCConfig)
|
||||
this.myPeerConnection.onaddstream = this.onaddstream.bind(this)
|
||||
}
|
||||
|
||||
close () {
|
||||
if (this.myPeerConnection !== null) {
|
||||
this.myPeerConnection.close()
|
||||
}
|
||||
this.myPeerConnection = null
|
||||
}
|
||||
|
||||
async prepareCall () {
|
||||
await this.init()
|
||||
this.newConnection()
|
||||
this.initiator = true
|
||||
this.myPeerConnection.addStream(this.stream)
|
||||
this.myPeerConnection.onicecandidate = this.onicecandidate.bind(this)
|
||||
this.offer = await this.myPeerConnection.createOffer()
|
||||
this.myPeerConnection.setLocalDescription(this.offer)
|
||||
return btoa(JSON.stringify(this.offer))
|
||||
}
|
||||
|
||||
|
||||
async acceptCall (infoCall) {
|
||||
const offer = JSON.parse(atob(infoCall.rtcOffer))
|
||||
this.newConnection()
|
||||
this.initiator = false
|
||||
this.stream = await navigator.mediaDevices.getUserMedia(constraints)
|
||||
this.myPeerConnection.onicecandidate = this.onicecandidate.bind(this)
|
||||
this.myPeerConnection.addStream(this.stream)
|
||||
this.offer = new RTCSessionDescription(offer)
|
||||
this.myPeerConnection.setRemoteDescription(this.offer)
|
||||
this.answer = await this.myPeerConnection.createAnswer()
|
||||
this.myPeerConnection.setLocalDescription(this.answer)
|
||||
return btoa(JSON.stringify(this.answer))
|
||||
}
|
||||
|
||||
async onReceiveAnswer (answerData) {
|
||||
const answerObj = JSON.parse(atob(answerData))
|
||||
this.answer = new RTCSessionDescription(answerObj)
|
||||
this.myPeerConnection.setRemoteDescription(this.answer)
|
||||
}
|
||||
|
||||
onicecandidate (event) {
|
||||
if (event.candidate !== undefined) {
|
||||
this.myCandidates.push(event.candidate)
|
||||
if (this.listener['onCandidate'] !== undefined) {
|
||||
const candidates = this.getAvailableCandidates()
|
||||
for (let func of this.listener['onCandidate']) {
|
||||
func(candidates)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAvailableCandidates() {
|
||||
const candidates = btoa(JSON.stringify(this.myCandidates))
|
||||
this.myCandidates = []
|
||||
return candidates
|
||||
}
|
||||
|
||||
addIceCandidates (candidatesRaw) {
|
||||
if (this.myPeerConnection !== null) {
|
||||
const candidates = JSON.parse(atob(candidatesRaw))
|
||||
candidates.forEach((candidate) => {
|
||||
if (candidate !== null) {
|
||||
this.myPeerConnection.addIceCandidate(candidate)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
addEventListener (eventName, callBack) {
|
||||
if (eventName === 'onCandidate') {
|
||||
if (this.listener[eventName] === undefined) {
|
||||
this.listener[eventName] = []
|
||||
}
|
||||
this.listener[eventName].push(callBack)
|
||||
callBack(this.getAvailableCandidates())
|
||||
}
|
||||
}
|
||||
|
||||
onaddstream (event) {
|
||||
this.audio.srcObject = event.stream
|
||||
this.audio.play()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* eslint-disable */
|
||||
(async function () {
|
||||
})()
|
||||
|
||||
export default VoiceRTC
|
||||
4
HTML/gcphone/src/assets/css/font-awesome.min.css
vendored
Normal file
BIN
HTML/gcphone/src/assets/fonts/FontAwesome.otf
Normal file
BIN
HTML/gcphone/src/assets/fonts/fontawesome-webfont.eot
Normal file
2671
HTML/gcphone/src/assets/fonts/fontawesome-webfont.svg
Normal file
|
After Width: | Height: | Size: 434 KiB |
BIN
HTML/gcphone/src/assets/fonts/fontawesome-webfont.ttf
Normal file
BIN
HTML/gcphone/src/assets/fonts/fontawesome-webfont.woff
Normal file
BIN
HTML/gcphone/src/assets/fonts/fontawesome-webfont.woff2
Normal file
BIN
HTML/gcphone/src/assets/img/back001.jpg
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
HTML/gcphone/src/assets/img/call.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
HTML/gcphone/src/assets/img/contacts.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
HTML/gcphone/src/assets/img/phone.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
HTML/gcphone/src/assets/img/settings.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
HTML/gcphone/src/assets/img/sms.png
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
HTML/gcphone/src/assets/logo.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
135
HTML/gcphone/src/components/App9GAG/index.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<div class="phone_app">
|
||||
<PhoneTitle :title="'9 GAG (' + currentSelectPost + ')'" backgroundColor="#000" @back="quit"/>
|
||||
<div class='phone_content' @click="onClick">
|
||||
<div class="post" v-if="currentPost !== undefined">
|
||||
<h1 class="post-title">{{ currentPost.title }}</h1>
|
||||
<div class="post-content">
|
||||
<video class="post-video" ref="video" v-if="currentPost.images.image460svwm !== undefined" autoplay loop :src="currentPost.images.image460svwm.url">
|
||||
</video>
|
||||
<img class="post-image" v-else :src="currentPost.images.image460.url" alt="">
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="loading">
|
||||
<div>CHARGEMENT</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PhoneTitle from './../PhoneTitle'
|
||||
export default {
|
||||
components: {
|
||||
PhoneTitle
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
nextCursor: 'c=10',
|
||||
currentSelectPost: 0,
|
||||
posts: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentPost () {
|
||||
if (this.posts && this.posts.length > this.currentSelectPost) {
|
||||
return this.posts[this.currentSelectPost]
|
||||
}
|
||||
this.loadItems()
|
||||
return undefined
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async loadItems () {
|
||||
let url = 'https://9gag.com/v1/group-posts/group/default/type/hot?' + this.nextCursor
|
||||
const request = await fetch(url)
|
||||
const data = await request.json()
|
||||
this.posts.push(...data.data.posts)
|
||||
this.nextCursor = data.data.nextCursor
|
||||
},
|
||||
previewPost () {
|
||||
if (this.currentSelectPost === 0) {
|
||||
return 0
|
||||
}
|
||||
this.currentSelectPost -= 1
|
||||
setTimeout(() => {
|
||||
if (this.$refs.video !== undefined) {
|
||||
this.$refs.video.volume = 0.15
|
||||
}
|
||||
}, 200)
|
||||
},
|
||||
nextPost () {
|
||||
this.currentSelectPost += 1
|
||||
setTimeout(() => {
|
||||
if (this.$refs.video !== undefined) {
|
||||
this.$refs.video.volume = 0.15
|
||||
}
|
||||
}, 200)
|
||||
},
|
||||
onClick ($event) {
|
||||
if ($event.offsetX < 200) {
|
||||
this.previewPost()
|
||||
} else {
|
||||
this.nextPost()
|
||||
}
|
||||
},
|
||||
quit: function () {
|
||||
this.$router.push({ name: 'home' })
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.$bus.$on('keyUpArrowLeft', this.previewPost)
|
||||
this.$bus.$on('keyUpArrowRight', this.nextPost)
|
||||
this.$bus.$on('keyUpBackspace', this.quit)
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
this.$bus.$off('keyUpArrowLeft', this.previewPost)
|
||||
this.$bus.$off('keyUpArrowRight', this.nextPost)
|
||||
this.$bus.$off('keyUpBackspace', this.quit)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.post{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
|
||||
.post-title {
|
||||
padding-left: 12px;
|
||||
font-size: 18px;
|
||||
height: 18px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.post-content{
|
||||
display: flex;
|
||||
width: 390px;
|
||||
height: 670px;
|
||||
}
|
||||
|
||||
.post-video, .post-image{
|
||||
object-fit: contain;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.loading{
|
||||
height: 100%;
|
||||
background-color: black;
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
div {
|
||||
text-align: center;
|
||||
margin-bottom: 36px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
147
HTML/gcphone/src/components/Appels/Appels.vue
Normal file
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<div style="width: 326px; height: 743px;" class="phone_app">
|
||||
<PhoneTitle :title="IntlString('APP_PHONE_TITLE')" v-on:back="onBackspace" />
|
||||
<div class="content">
|
||||
<component :is="subMenu[currentMenuIndex].Comp" />
|
||||
</div>
|
||||
<div class="subMenu">
|
||||
<div
|
||||
class="subMenu-elem"
|
||||
:style="getColorItem(i)"
|
||||
v-for="(Comp, i) of subMenu"
|
||||
:key="i"
|
||||
@click="swapMenu(i)"
|
||||
>
|
||||
<i class="subMenu-icon fa" :class="['fa-' + Comp.icon]" @click.stop="swapMenu(i)"></i>
|
||||
<span class="subMenu-name" @click.stop="swapMenu(i)">{{Comp.name}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
import PhoneTitle from './../PhoneTitle'
|
||||
import AppelsFavoris from './AppelsFavoris'
|
||||
import AppelsContacts from './AppelsContacts'
|
||||
import AppelsRecents from './AppelsRecents'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PhoneTitle
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
currentMenuIndex: 1
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'useMouse', 'themeColor']),
|
||||
subMenu () {
|
||||
return [{
|
||||
Comp: AppelsFavoris,
|
||||
name: this.IntlString('APP_PHONE_MENU_FAVORITES'),
|
||||
icon: 'star'
|
||||
}, {
|
||||
Comp: AppelsRecents,
|
||||
name: this.IntlString('APP_PHONE_MENU_RECENTS'),
|
||||
icon: 'clock-o'
|
||||
}, {
|
||||
Comp: AppelsContacts,
|
||||
name: this.IntlString('APP_PHONE_MENU_CONTACTS'),
|
||||
icon: 'user'
|
||||
}]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getColorItem (index) {
|
||||
if (this.currentMenuIndex === index) {
|
||||
return {
|
||||
color: this.themeColor
|
||||
}
|
||||
}
|
||||
return {}
|
||||
},
|
||||
swapMenu (index) {
|
||||
this.currentMenuIndex = index
|
||||
},
|
||||
onLeft () {
|
||||
this.currentMenuIndex = Math.max(this.currentMenuIndex - 1, 0)
|
||||
},
|
||||
onRight () {
|
||||
this.currentMenuIndex = Math.min(this.currentMenuIndex + 1, this.subMenu.length - 1)
|
||||
},
|
||||
onBackspace: function () {
|
||||
if (this.ignoreControls === true) return
|
||||
this.$router.push({ name: 'home' })
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpBackspace', this.onBackspace)
|
||||
this.$bus.$on('keyUpArrowLeft', this.onLeft)
|
||||
this.$bus.$on('keyUpArrowRight', this.onRight)
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpBackspace', this.onBackspace)
|
||||
this.$bus.$off('keyUpArrowLeft', this.onLeft)
|
||||
this.$bus.$off('keyUpArrowRight', this.onRight)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.screen{
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.title{
|
||||
padding-left: 16px;
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
background-color: #2c3e50;
|
||||
}
|
||||
.content{
|
||||
height: calc(100% - 68px);
|
||||
overflow-y: auto;
|
||||
width: 337px;
|
||||
}
|
||||
.subMenu{
|
||||
border-top: 1px solid rgba(0,0,0,0.24);
|
||||
display: flex;
|
||||
height: 56px;
|
||||
}
|
||||
.subMenu-elem {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
line-height: 56px;
|
||||
height: 56px;
|
||||
display: flex;
|
||||
color: #959595;
|
||||
flex-direction: column;
|
||||
}
|
||||
.subMenu-elem-select, .subMenu-elem:hover {
|
||||
color: #007aff;
|
||||
}
|
||||
.subMenu-icon{
|
||||
margin-top: 6px;
|
||||
font-size: 22px;
|
||||
line-height: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
.subMenu-name{
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
height: 14px;
|
||||
line-height: 14px;
|
||||
}
|
||||
</style>
|
||||
319
HTML/gcphone/src/components/Appels/AppelsActive.vue
Normal file
@@ -0,0 +1,319 @@
|
||||
<template>
|
||||
<div style="width: 326px; height: 743px;" class="phone_app">
|
||||
<div class="backblur" v-bind:style="{background: 'url(' + backgroundURL +')'}"></div>
|
||||
<InfoBare />
|
||||
<div class="num">{{appelsDisplayNumber}}</div>
|
||||
<div class="contactName">{{appelsDisplayName}}</div>
|
||||
|
||||
<div class="time"></div>
|
||||
<div class="time-display">{{timeDisplay}}</div>
|
||||
|
||||
<div
|
||||
v-if="useMouse && status === 0"
|
||||
class="ignore"
|
||||
@click.stop="onIgnoreCall">
|
||||
{{ IntlString('APP_PHONE_CALL_IGNORE')}}
|
||||
</div>
|
||||
|
||||
<div class="actionbox">
|
||||
<div class="action raccrocher" :class="{disableTrue: status === 0 && select !== 0}"
|
||||
@click.stop="raccrocher"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" @click.stop="raccrocher">
|
||||
<g transform="rotate(135, 12, 12)">
|
||||
<path d="M6.62,10.79C8.06,13.62 10.38,15.94 13.21,17.38L15.41,15.18C15.69,14.9 16.08,14.82 16.43,14.93C17.55,15.3 18.75,15.5 20,15.5A1,1 0 0,1 21,16.5V20A1,1 0 0,1 20,21A17,17 0 0,1 3,4A1,1 0 0,1 4,3H7.5A1,1 0 0,1 8.5,4C8.5,5.25 8.7,6.45 9.07,7.57C9.18,7.92 9.1,8.31 8.82,8.59L6.62,10.79Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="action deccrocher" v-if="status === 0" :class="{disableFalse: status === 0 && select !== 1}"
|
||||
@click.stop="deccrocher"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" @click.stop="deccrocher">
|
||||
<g transform="rotate(0, 12, 12)">
|
||||
<path d="M6.62,10.79C8.06,13.62 10.38,15.94 13.21,17.38L15.41,15.18C15.69,14.9 16.08,14.82 16.43,14.93C17.55,15.3 18.75,15.5 20,15.5A1,1 0 0,1 21,16.5V20A1,1 0 0,1 20,21A17,17 0 0,1 3,4A1,1 0 0,1 4,3H7.5A1,1 0 0,1 8.5,4C8.5,5.25 8.7,6.45 9.07,7.57C9.18,7.92 9.1,8.31 8.82,8.59L6.62,10.79Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// eslint-disable-next-line
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
import InfoBare from './../InfoBare'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
InfoBare
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
time: -1,
|
||||
intervalNum: undefined,
|
||||
select: -1,
|
||||
status: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['acceptCall', 'rejectCall', 'ignoreCall']),
|
||||
onBackspace () {
|
||||
if (this.status === 1) {
|
||||
this.onRejectCall()
|
||||
} else {
|
||||
this.onIgnoreCall()
|
||||
}
|
||||
},
|
||||
onEnter () {
|
||||
if (this.status === 0) {
|
||||
if (this.select === 0) {
|
||||
this.onRejectCall()
|
||||
} else {
|
||||
this.onAcceptCall()
|
||||
}
|
||||
}
|
||||
},
|
||||
raccrocher () {
|
||||
this.onRejectCall()
|
||||
},
|
||||
deccrocher () {
|
||||
if (this.status === 0) {
|
||||
this.onAcceptCall()
|
||||
}
|
||||
},
|
||||
onLeft () {
|
||||
if (this.status === 0) {
|
||||
this.select = 0
|
||||
}
|
||||
},
|
||||
onRight () {
|
||||
if (this.status === 0) {
|
||||
this.select = 1
|
||||
}
|
||||
},
|
||||
updateTime () {
|
||||
this.time += 1
|
||||
},
|
||||
onRejectCall () {
|
||||
this.rejectCall()
|
||||
this.$phoneAPI.setIgnoreFocus(false)
|
||||
},
|
||||
onAcceptCall () {
|
||||
this.acceptCall()
|
||||
this.$phoneAPI.setIgnoreFocus(true)
|
||||
},
|
||||
onIgnoreCall () {
|
||||
this.ignoreCall()
|
||||
this.$phoneAPI.setIgnoreFocus(false)
|
||||
this.$router.push({ name: 'home' })
|
||||
},
|
||||
startTimer () {
|
||||
if (this.intervalNum === undefined) {
|
||||
this.time = 0
|
||||
this.intervalNum = setInterval(this.updateTime, 1000)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
appelsInfo () {
|
||||
if (this.appelsInfo === null) return
|
||||
if (this.appelsInfo.is_accepts === true) {
|
||||
this.status = 1
|
||||
this.$phoneAPI.setIgnoreFocus(true)
|
||||
this.startTimer()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'backgroundURL', 'useMouse', 'appelsInfo', 'appelsDisplayName', 'appelsDisplayNumber', 'myPhoneNumber']),
|
||||
timeDisplay () {
|
||||
if (this.time < 0) {
|
||||
return '. . .'
|
||||
}
|
||||
const min = Math.floor(this.time / 60)
|
||||
let sec = this.time % 60
|
||||
if (sec < 10) {
|
||||
sec = '0' + sec
|
||||
}
|
||||
return `${min}:${sec}`
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
if (this.appelsInfo !== null && this.appelsInfo.initiator === true) {
|
||||
this.status = 1
|
||||
this.$phoneAPI.setIgnoreFocus(true)
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
this.$bus.$on('keyUpArrowLeft', this.onLeft)
|
||||
this.$bus.$on('keyUpArrowRight', this.onRight)
|
||||
}
|
||||
this.$bus.$on('keyUpBackspace', this.onBackspace)
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpBackspace', this.onBackspace)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
this.$bus.$off('keyUpArrowLeft', this.onLeft)
|
||||
this.$bus.$off('keyUpArrowRight', this.onRight)
|
||||
if (this.intervalNum !== undefined) {
|
||||
window.clearInterval(this.intervalNum)
|
||||
}
|
||||
this.$phoneAPI.setIgnoreFocus(false)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.backblur{
|
||||
top: -6px;
|
||||
left: -6px;
|
||||
right:-6px;
|
||||
bottom: -6px;
|
||||
position: absolute;
|
||||
background-size: cover !important;
|
||||
filter: blur(6px);
|
||||
}
|
||||
.num{
|
||||
position: absolute;
|
||||
text-shadow: 0px 0px 15px black, 0px 0px 15px black;
|
||||
top: 60px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
text-align: center;
|
||||
font-size: 46px;
|
||||
}
|
||||
.contactName{
|
||||
position: absolute;
|
||||
text-shadow: 0px 0px 15px black, 0px 0px 15px black;
|
||||
top: 100px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
text-align: center;
|
||||
margin-top: 16px;
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.time{
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
top: 280px;
|
||||
left: 0px;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
border-top: 2px solid white;
|
||||
border-radius: 50%;
|
||||
animation: rond 1.8s infinite linear;
|
||||
}
|
||||
.time-display{
|
||||
text-shadow: 0px 0px 15px black, 0px 0px 15px black;
|
||||
position: relative;
|
||||
top: 187px;
|
||||
line-height: 20px;
|
||||
left: 0px;
|
||||
width: 150px;
|
||||
height: 91px;
|
||||
color: white;
|
||||
font-size: 36px;
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.actionbox {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
bottom: 70px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.action {
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.raccrocher {
|
||||
background-color: #fd3d2e;
|
||||
height: 70px;
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.raccrocher:hover {
|
||||
background-color: #ffffff !important;
|
||||
height: 90px;
|
||||
width: 90px;
|
||||
|
||||
}
|
||||
|
||||
.deccrocher {
|
||||
background-color: #4ddb62;
|
||||
height: 70px;
|
||||
width: 70px;
|
||||
}
|
||||
.deccrocher:hover {
|
||||
background-color: #ffffff !important;
|
||||
height: 90px;
|
||||
width: 90px;
|
||||
|
||||
}
|
||||
|
||||
.disableTrue {
|
||||
background-color: #fd3d2e;
|
||||
height: 70px;
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.disable {
|
||||
background-color: #4ddb62;
|
||||
height: 70px;
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.action svg{
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
margin: 5px;
|
||||
fill: #EEE;
|
||||
}
|
||||
|
||||
|
||||
.ignore {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
bottom: 220px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
border-radius: 20px;
|
||||
text-align: center;
|
||||
left: 0;
|
||||
right: 0;
|
||||
justify-content: space-around;
|
||||
background-color: #4d4d4d;
|
||||
width: 70%;
|
||||
left: 15%;
|
||||
color: #CCC;
|
||||
}
|
||||
.ignore:hover {
|
||||
background-color: #818080;
|
||||
}
|
||||
|
||||
@keyframes rond {
|
||||
from {
|
||||
rotate: 0deg
|
||||
}
|
||||
to {
|
||||
rotate: 360deg
|
||||
}
|
||||
}
|
||||
</style>
|
||||
55
HTML/gcphone/src/components/Appels/AppelsContacts.vue
Normal file
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<div style="width: 326px; height: 743px;">
|
||||
<list :list='contactsList' :showHeader="false" v-on:select="onSelect"></list>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
import { generateColorForStr } from '@/Utils'
|
||||
import List from './../List.vue'
|
||||
|
||||
export default {
|
||||
name: 'Contacts',
|
||||
components: { List },
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['startCall']),
|
||||
onSelect (itemSelect) {
|
||||
if (itemSelect !== undefined) {
|
||||
if (itemSelect.custom === true) {
|
||||
this.$router.push({name: 'appels.number'})
|
||||
} else {
|
||||
this.startCall({ numero: itemSelect.number })
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'contacts']),
|
||||
contactsList () {
|
||||
return [{
|
||||
display: this.IntlString('APP_PHONE_ENTER_NUMBER'),
|
||||
letter: '#',
|
||||
backgroundColor: '#D32F2F',
|
||||
custom: true
|
||||
}, ...this.contacts.slice(0).map(c => {
|
||||
c.backgroundColor = generateColorForStr(c.number)
|
||||
return c
|
||||
})]
|
||||
}
|
||||
},
|
||||
created () {
|
||||
|
||||
},
|
||||
beforeDestroy () {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
49
HTML/gcphone/src/components/Appels/AppelsFavoris.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div>
|
||||
<list :list='callList' :showHeader="false" :disable='ignoreControls' v-on:select="onSelect"></list>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import List from './../List.vue'
|
||||
import Modal from '@/components/Modal/index.js'
|
||||
|
||||
export default {
|
||||
name: 'Favoris',
|
||||
components: { List },
|
||||
data () {
|
||||
return {
|
||||
ignoreControls: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['config']),
|
||||
callList () {
|
||||
return this.config.serviceCall || []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSelect (itemSelect) {
|
||||
if (this.ignoreControls === true) return
|
||||
this.ignoreControls = true
|
||||
Modal.CreateModal({choix: [...itemSelect.subMenu, {title: 'Abbrechen'}]}).then(rep => {
|
||||
this.ignoreControls = false
|
||||
if (rep.title === 'Abbrechen') return
|
||||
this.$phoneAPI.callEvent(rep.eventName, rep.type)
|
||||
this.$router.push({name: 'home'})
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
},
|
||||
|
||||
beforeDestroy () {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
261
HTML/gcphone/src/components/Appels/AppelsNumber.vue
Normal file
@@ -0,0 +1,261 @@
|
||||
|
||||
|
||||
<template>
|
||||
<div class="phone_app">
|
||||
<PhoneTitle :title="IntlString('APP_PHONE_TITLE')" @back="quit" />
|
||||
<div class="content">
|
||||
<div class="number">
|
||||
{{ numeroFormat }}
|
||||
<span class="deleteNumber" @click.stop="deleteNumber"></span>
|
||||
</div>
|
||||
|
||||
<div class="keyboard">
|
||||
<div
|
||||
class="key"
|
||||
v-for="(key, i) of keyInfo" :key="key.primary"
|
||||
:class="{'key-select': i === keySelect, 'keySpe': key.isNotNumber === true}"
|
||||
@click.stop="onPressKey(key)"
|
||||
>
|
||||
<span @click.stop="onPressKey(key)" class="key-primary">{{key.primary}}</span>
|
||||
<span @click.stop="onPressKey(key)" class="key-secondary">{{key.secondary}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="call">
|
||||
<div class="call-btn" :class="{'active': keySelect === 12}"
|
||||
@click.stop="onPressCall">
|
||||
<svg viewBox="0 0 24 24" @click.stop="onPressCall">
|
||||
<g transform="rotate(0, 12, 12)">
|
||||
<path d="M6.62,10.79C8.06,13.62 10.38,15.94 13.21,17.38L15.41,15.18C15.69,14.9 16.08,14.82 16.43,14.93C17.55,15.3 18.75,15.5 20,15.5A1,1 0 0,1 21,16.5V20A1,1 0 0,1 20,21A17,17 0 0,1 3,4A1,1 0 0,1 4,3H7.5A1,1 0 0,1 8.5,4C8.5,5.25 8.7,6.45 9.07,7.57C9.18,7.92 9.1,8.31 8.82,8.59L6.62,10.79Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import PhoneTitle from './../PhoneTitle'
|
||||
export default {
|
||||
components: {
|
||||
PhoneTitle
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
numero: '',
|
||||
keyInfo: [
|
||||
{primary: '1', secondary: ''},
|
||||
{primary: '2', secondary: 'abc'},
|
||||
{primary: '3', secondary: 'def'},
|
||||
{primary: '4', secondary: 'ghi'},
|
||||
{primary: '5', secondary: 'jkl'},
|
||||
{primary: '6', secondary: 'mmo'},
|
||||
{primary: '7', secondary: 'pqrs'},
|
||||
{primary: '8', secondary: 'tuv'},
|
||||
{primary: '9', secondary: 'wxyz'},
|
||||
{primary: '-', secondary: '', isNotNumber: true},
|
||||
{primary: '0', secondary: '+'},
|
||||
{primary: '#', secondary: '', isNotNumber: true}
|
||||
],
|
||||
keySelect: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['startCall']),
|
||||
onLeft () {
|
||||
this.keySelect = Math.max(this.keySelect - 1, 0)
|
||||
},
|
||||
onRight () {
|
||||
this.keySelect = Math.min(this.keySelect + 1, 11)
|
||||
},
|
||||
onDown () {
|
||||
this.keySelect = Math.min(this.keySelect + 3, 12)
|
||||
},
|
||||
onUp () {
|
||||
if (this.keySelect > 2) {
|
||||
if (this.keySelect === 12) {
|
||||
this.keySelect = 10
|
||||
} else {
|
||||
this.keySelect = this.keySelect - 3
|
||||
}
|
||||
}
|
||||
},
|
||||
onEnter () {
|
||||
if (this.keySelect === 12) {
|
||||
if (this.numero.length > 0) {
|
||||
this.startCall({ numero: this.numeroFormat })
|
||||
}
|
||||
} else {
|
||||
this.numero += this.keyInfo[this.keySelect].primary
|
||||
}
|
||||
},
|
||||
onBackspace: function () {
|
||||
if (this.ignoreControls === true) return
|
||||
if (this.numero.length !== 0) {
|
||||
this.numero = this.numero.slice(0, -1)
|
||||
} else {
|
||||
history.back()
|
||||
}
|
||||
},
|
||||
deleteNumber () {
|
||||
if (this.numero.length !== 0) {
|
||||
this.numero = this.numero.slice(0, -1)
|
||||
}
|
||||
},
|
||||
onPressKey (key) {
|
||||
this.numero = this.numero + key.primary
|
||||
},
|
||||
onPressCall () {
|
||||
this.startCall({ numero: this.numeroFormat })
|
||||
},
|
||||
quit () {
|
||||
history.back()
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'useMouse', 'useFormatNumberFrance']),
|
||||
numeroFormat () {
|
||||
if (this.useFormatNumberFrance === true) {
|
||||
return this.numero
|
||||
}
|
||||
const l = this.numero.startsWith('#') ? 4 : 3
|
||||
if (this.numero.length > l) {
|
||||
return this.numero.slice(0, l) + '-' + this.numero.slice(l)
|
||||
} else {
|
||||
return this.numero
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpBackspace', this.onBackspace)
|
||||
this.$bus.$on('keyUpArrowLeft', this.onLeft)
|
||||
this.$bus.$on('keyUpArrowRight', this.onRight)
|
||||
this.$bus.$on('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$on('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
} else {
|
||||
this.keySelect = -1
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpBackspace', this.onBackspace)
|
||||
this.$bus.$off('keyUpArrowLeft', this.onLeft)
|
||||
this.$bus.$off('keyUpArrowRight', this.onRight)
|
||||
this.$bus.$off('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$off('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
|
||||
.number{
|
||||
margin-top: 140px;
|
||||
width: 100%;
|
||||
height: 52px;
|
||||
font-size: 26px;
|
||||
line-height: 52px;
|
||||
text-align: right;
|
||||
padding-right: 8px;
|
||||
border-bottom: 1px solid #C0C0C0;
|
||||
margin-bottom: 8px;
|
||||
box-shadow: 0px -6px 12px 0px rgba(189,189,189,0.4);
|
||||
position: relative;
|
||||
padding-right: 60px;
|
||||
}
|
||||
.keyboard {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
}
|
||||
.key {
|
||||
position: relative;
|
||||
flex: 1 1 33.33%;
|
||||
text-align: center;
|
||||
height: 96px;
|
||||
}
|
||||
|
||||
.key-select::after, .key:hover::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: calc(50% - 45px);
|
||||
left: calc(50% - 45px);
|
||||
display: block;
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
background: radial-gradient(rgba(0, 0, 0, 0.04), rgba(0, 0, 0, 0.16));
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.key-primary {
|
||||
display: block;
|
||||
font-size: 36px;
|
||||
color: black;
|
||||
line-height: 22px;
|
||||
padding-top: 36px;
|
||||
}
|
||||
.keySpe .key-primary {
|
||||
color: #2c3e50;
|
||||
line-height: 96px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.key-secondary {
|
||||
text-transform: uppercase;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: black;
|
||||
line-height: 12px;
|
||||
padding-top: 6px;
|
||||
}
|
||||
.call {
|
||||
margin-top: 18px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.call-btn {
|
||||
margin-top: -29px;
|
||||
height: 70px;
|
||||
width: 70px;
|
||||
border-radius: 50%;
|
||||
background-color: #52d66a;
|
||||
}
|
||||
.call-btn.active, .call-btn:hover {
|
||||
background-color: #43a047;
|
||||
}
|
||||
.call-btn svg {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin: 10px;
|
||||
fill: #EEE;
|
||||
}
|
||||
.deleteNumber {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
background: #2C2C2C;
|
||||
top: 16px;
|
||||
right: 12px;
|
||||
height: 18px;
|
||||
width: 32px;
|
||||
padding: 0;
|
||||
}
|
||||
.deleteNumber:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -5px;
|
||||
top:0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 9px 5px 9px 0;
|
||||
border-color: transparent #2C2C2C transparent transparent;
|
||||
}
|
||||
</style>
|
||||
251
HTML/gcphone/src/components/Appels/AppelsRecents.vue
Normal file
@@ -0,0 +1,251 @@
|
||||
<template>
|
||||
<div class="phone_app">
|
||||
<div class="elements">
|
||||
<div class="element" :class="{'active': selectIndex === key}" v-for='(histo, key) in historique' :key="key"
|
||||
@click.stop="selectItem(histo)"
|
||||
>
|
||||
|
||||
<!--<div @click.stop="selectItem(histo)" class="elem-pic" :style="stylePuce(histo)">{{histo.letter}}</div>-->
|
||||
<img style="width: 32px; margin-left: 10px;" src="/html/static/img/icons_app/borrado.png" alt="Logotipo APR2">
|
||||
|
||||
|
||||
<div @click.stop="selectItem(histo)" class="elem-content">
|
||||
<div style="font-size: 20px; font-weight: 400; margin-top: 20px;" @click.stop="selectItem(histo)" class="elem-content-p">{{histo.display}}</div>
|
||||
|
||||
<div @click.stop="selectItem(histo)" class="elem-content-s">
|
||||
<div
|
||||
@click.stop="selectItem(histo)"
|
||||
class="elem-histo-pico"
|
||||
:class="{'reject': hc.accept === false}"
|
||||
v-for="(hc, i) in histo.lastCall" :key="i">
|
||||
<svg @click.stop="selectItem(histo)" v-if="hc.accepts === 1 && hc.incoming === 1" viewBox="0 0 24 24" fill="#c5c5c7">
|
||||
<path d="M9,5v2h6.59L4,18.59L5.41,20L17,8.41V15h2V5H9z"/>
|
||||
</svg>
|
||||
<svg @click.stop="selectItem(histo)" v-else-if="hc.accepts === 1 && hc.incoming === 0" viewBox="0 0 24 24" fill="#c5c5c7">
|
||||
<path d="M20,5.41L18.59,4L7,15.59V9H5v10h10v-2H8.41L20,5.41z"/>
|
||||
</svg>
|
||||
<svg @click.stop="selectItem(histo)" v-else-if="hc.accepts === 0 && hc.incoming === 1" viewBox="0 0 24 24" fill="#c5c5c7">
|
||||
<path @click.stop="selectItem(histo)" d="M3,8.41l9,9l7-7V15h2V7h-8v2h4.59L12,14.59L4.41,7L3,8.41z"/>
|
||||
</svg>
|
||||
<svg @click.stop="selectItem(histo)" v-else-if="hc.accepts === 0 && hc.incoming === 0" viewBox="0 0 24 24" fill="#c5c5c7">
|
||||
<path d="M19.59,7L12,14.59L6.41,9H11V7H3v8h2v-4.59l7,7l9-9L19.59,7z"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!--<div v-if="histo.lastCall.length !==0" class="lastCall">
|
||||
<timeago :since='histo.lastCall[0].date' :auto-update="20"></timeago>
|
||||
</div>-->
|
||||
</div>
|
||||
<div style="float: right; margin-top: -43px;" v-if="histo.lastCall.length !==0" class="lastCall">
|
||||
<timeago class="time" :since='histo.lastCall[0].date' :auto-update="20"></timeago>
|
||||
</div>
|
||||
</div>
|
||||
<!--<div class="elem-icon" @click.stop="selectItem(histo)">
|
||||
<i class="fa fa-phone" @click.stop="selectItem(histo)"></i>
|
||||
</div>-->
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
import { groupBy, generateColorForStr } from '@/Utils'
|
||||
import Modal from '@/components/Modal/index.js'
|
||||
|
||||
export default {
|
||||
name: 'Recents',
|
||||
components: {},
|
||||
data () {
|
||||
return {
|
||||
ignoreControls: false,
|
||||
selectIndex: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['startCall', 'appelsDeleteHistorique', 'appelsDeleteAllHistorique']),
|
||||
getContact (num) {
|
||||
const find = this.contacts.find(e => e.number === num)
|
||||
return find
|
||||
},
|
||||
scrollIntoViewIfNeeded: function () {
|
||||
this.$nextTick(() => {
|
||||
this.$el.querySelector('.active').scrollIntoViewIfNeeded()
|
||||
})
|
||||
},
|
||||
onUp () {
|
||||
if (this.ignoreControls === true) return
|
||||
this.selectIndex = Math.max(0, this.selectIndex - 1)
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
onDown () {
|
||||
if (this.ignoreControls === true) return
|
||||
this.selectIndex = Math.min(this.historique.length - 1, this.selectIndex + 1)
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
async selectItem (item) {
|
||||
const numero = item.num
|
||||
const isValid = numero.startsWith('#') === false
|
||||
this.ignoreControls = true
|
||||
let choix = [
|
||||
{id: 1, title: this.IntlString('APP_PHONE_DELETE'), icons: 'fa-trash', color: 'orange'},
|
||||
{id: 2, title: this.IntlString('APP_PHONE_DELETE_ALL'), icons: 'fa-trash', color: 'red'},
|
||||
{id: 3, title: this.IntlString('CANCEL'), icons: 'fa-undo'}
|
||||
]
|
||||
if (isValid === true) {
|
||||
choix = [{id: 0, title: this.IntlString('APP_PHONE_CALL'), icons: 'fa-phone'}, ...choix]
|
||||
}
|
||||
const rep = await Modal.CreateModal({ choix })
|
||||
this.ignoreControls = false
|
||||
switch (rep.id) {
|
||||
case 0:
|
||||
this.startCall({ numero })
|
||||
break
|
||||
case 1:
|
||||
this.appelsDeleteHistorique({ numero })
|
||||
break
|
||||
case 2 :
|
||||
this.appelsDeleteAllHistorique()
|
||||
}
|
||||
},
|
||||
async onEnter () {
|
||||
if (this.ignoreControls === true) return
|
||||
this.selectItem(this.historique[this.selectIndex])
|
||||
},
|
||||
stylePuce (data) {
|
||||
data = data || {}
|
||||
if (data.icon !== undefined) {
|
||||
return {
|
||||
backgroundImage: `url(${data.icon})`,
|
||||
backgroundSize: 'cover',
|
||||
color: 'rgba(0,0,0,0)'
|
||||
}
|
||||
}
|
||||
return {
|
||||
color: data.color || this.color,
|
||||
backgroundColor: data.backgroundColor || this.backgroundColor,
|
||||
borderRadius: '50%'
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'useMouse', 'appelsHistorique', 'contacts']),
|
||||
historique () {
|
||||
let grpHist = groupBy(this.appelsHistorique, 'num')
|
||||
let hist = []
|
||||
for (let key in grpHist) {
|
||||
const hg = grpHist[key]
|
||||
const histoByDate = hg.map(e => {
|
||||
e.date = new Date(e.time)
|
||||
return e
|
||||
}).sort((a, b) => {
|
||||
return b.date - a.date
|
||||
}).slice(0, 6)
|
||||
const contact = this.getContact(key) || { letter: '#' }
|
||||
hist.push({
|
||||
num: key,
|
||||
display: contact.display || key,
|
||||
lastCall: histoByDate,
|
||||
letter: contact.letter || contact.display[0],
|
||||
backgroundColor: contact.backgroundColor || generateColorForStr(key),
|
||||
icon: contact.icon
|
||||
})
|
||||
}
|
||||
hist.sort((a, b) => {
|
||||
return b.lastCall[0].time - a.lastCall[0].time
|
||||
})
|
||||
return hist
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$on('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
} else {
|
||||
this.selectIndex = -1
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$off('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.content {
|
||||
height: 100%;
|
||||
}
|
||||
.elements {
|
||||
overflow-y: auto;
|
||||
margin-left: -8px
|
||||
}
|
||||
.element{
|
||||
height: 58px;
|
||||
line-height: 58px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
margin: 14px 10px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.active, .element:hover {
|
||||
background: radial-gradient(rgba(3, 168, 244, 0.14), rgba(3, 169, 244, 0.26));
|
||||
}
|
||||
.elem-pic{
|
||||
margin-left: 12px;
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
text-align: center;
|
||||
line-height: 48px;
|
||||
font-weight: 700;
|
||||
border-radius: 50%;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.time{
|
||||
margin-right: 12px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #c8c7cb;
|
||||
}
|
||||
|
||||
.elem-content{
|
||||
margin-left: 12px;
|
||||
width: auto;
|
||||
flex-grow: 1;
|
||||
margin-top: 13px;
|
||||
}
|
||||
|
||||
.elem-content-p{
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
width: 153px;
|
||||
}
|
||||
.elem-content-s{
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
width: 137px;
|
||||
display: flex;
|
||||
}
|
||||
.elem-histo-pico {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.elem-histo-pico svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
.lastCall {
|
||||
padding-left: 4px;
|
||||
}
|
||||
.elem-icon{
|
||||
width: 28px;
|
||||
}
|
||||
</style>
|
||||
90
HTML/gcphone/src/components/Bank/Bank-viejo.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<div class="screen" @click="onBackspace">
|
||||
<div class='elements'>
|
||||
<img class="logo_maze" src="/html/static/img/app_bank/logo_mazebank.jpg">
|
||||
<div class="hr"></div>
|
||||
<div class='element'>
|
||||
<div class="element-content">
|
||||
<span>$ {{ bankAmontFormat }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['bankAmont']),
|
||||
bankAmontFormat () {
|
||||
return Intl.NumberFormat().format(this.bankAmont)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onBackspace () {
|
||||
this.$router.push({ name: 'home' })
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.$bus.$on('keyUpBackspace', this.onBackspace)
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpBackspace', this.onBackspace)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.screen{
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 18px;
|
||||
background-color: white;
|
||||
}
|
||||
.title{
|
||||
padding-left: 16px;
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
background-color: rgb(76, 175, 80);
|
||||
}
|
||||
.elements{
|
||||
display: flex;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
.hr{
|
||||
width: 100;
|
||||
height: 4px;
|
||||
margin-top: 4px;
|
||||
background-color: #EB202D;
|
||||
}
|
||||
.logo_maze {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.element-content{
|
||||
margin-top: 24px;
|
||||
display: block;
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
font-size: 24px;
|
||||
color: #EB202D;
|
||||
}
|
||||
</style>
|
||||
261
HTML/gcphone/src/components/Bank/Bank.vue
Normal file
@@ -0,0 +1,261 @@
|
||||
<template>
|
||||
<div style="width: 326px; height: 743px;" class="screen">
|
||||
|
||||
|
||||
<div class='elements'>
|
||||
<InfoBare style="width: 326px;top: -207px;margin-left: -17px;"/>
|
||||
<img class="logo_maze" src="/html/static/img/app_bank/fleeca_tar.png">
|
||||
<div class="num-tarj" >
|
||||
<span class="moneyTitle">{{ IntlString('APP_BANK_TITLE_BALANCE') }}</span>
|
||||
<span class="moneyTitle">{{ bankAmontFormat }}$</span>
|
||||
</div>
|
||||
|
||||
<div class="hr"></div>
|
||||
|
||||
<div class='element'>
|
||||
<div class="element-content">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="element-content" ref="form">
|
||||
<input style=" border-radius: 23px; font-size: 16px;" v-bind:class="{ select: 0 === currentSelect}" v-autofocus oninput="this.value = this.value.replace(/[^0-9.]/g, ''); this.value = this.value.replace(/(\..*)\./g, '$1');" ref="form0" v-model="id" class="paragonder" placeholder="ID">
|
||||
</div>
|
||||
|
||||
<div class="element-content">
|
||||
<input style=" border-radius: 23px; font-size: 16px;" v-bind:class="{ select: 1 === currentSelect}" oninput="this.value = this.value.replace(/[^0-9.]/g, ''); this.value = this.value.replace(/(\..*)\./g, '$1');" ref="form1" v-model="paratutar" class="paragonder" placeholder="$">
|
||||
<button v-bind:class="{ select: 2 === currentSelect}" ref="form2" id="gonder" @click.stop="paragonder" class="buton-transfer">{{ IntlString('APP_BANK_BUTTON_TRANSFER') }}</button><br/>
|
||||
<button v-bind:class="{ select: 3 === currentSelect}" ref="form3" id="iptal" @click.stop="iptal" class="buton-cancel">{{ IntlString('APP_BANK_BUTTON_CANCEL') }}</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<img class="logo_tarj_end" src="/html/static/img/app_bank/tarjetas.png">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
import InfoBare from '../InfoBare'
|
||||
export default {
|
||||
components: {
|
||||
InfoBare
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
id: '',
|
||||
paratutar: '',
|
||||
currentSelect: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['sendpara']),
|
||||
scrollIntoViewIfNeeded: function () {
|
||||
this.$nextTick(() => {
|
||||
document.querySelector('focus').scrollIntoViewIfNeeded()
|
||||
})
|
||||
},
|
||||
onBackspace () {
|
||||
this.$router.go(-1)
|
||||
},
|
||||
iptal () {
|
||||
// this.$router.push({path: '/messages'})
|
||||
this.$router.go(-1)
|
||||
},
|
||||
paragonder () {
|
||||
const paratutar = this.paratutar.trim()
|
||||
if (paratutar === '') return
|
||||
this.paratutar = ''
|
||||
this.sendpara({
|
||||
id: this.id,
|
||||
amount: paratutar
|
||||
})
|
||||
},
|
||||
onUp: function () {
|
||||
if ((this.currentSelect - 1) >= 0) {
|
||||
this.currentSelect = this.currentSelect - 1
|
||||
}
|
||||
this.$refs['form' + this.currentSelect].focus()
|
||||
console.log(this.currentSelect)
|
||||
},
|
||||
onDown () {
|
||||
if ((this.currentSelect + 1) <= 3) {
|
||||
this.currentSelect = this.currentSelect + 1
|
||||
}
|
||||
this.$refs['form' + this.currentSelect].focus()
|
||||
console.log(this.currentSelect)
|
||||
},
|
||||
onEnter () {
|
||||
if (this.ignoreControls === true) return
|
||||
if (this.currentSelect === 2) {
|
||||
this.paragonder()
|
||||
} else if (this.currentSelect === 0) {
|
||||
this.$phoneAPI.getReponseText().then(data => {
|
||||
let message = data.text.trim()
|
||||
this.id = message
|
||||
})
|
||||
} else if (this.currentSelect === 1) {
|
||||
this.$phoneAPI.getReponseText().then(data => {
|
||||
let message = data.text.trim()
|
||||
this.paratutar = message
|
||||
})
|
||||
} else if (this.currentSelect === 3) {
|
||||
this.iptal()
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['bankAmont', 'IntlString']),
|
||||
bankAmontFormat () {
|
||||
return Intl.NumberFormat().format(this.bankAmont)
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.display = this.$route.params.display
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$on('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
}
|
||||
this.$bus.$on('keyUpBackspace', this.onBackspace)
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$off('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
this.$bus.$off('keyUpBackspace', this.onBackspace)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.screen{
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 18px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.num-tarj{
|
||||
margin-top: -88px;
|
||||
margin-left: 50px
|
||||
}
|
||||
|
||||
.moneyTitle{
|
||||
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
||||
font-weight: 200;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.title{
|
||||
padding-left: 16px;
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
background-color: rgb(76, 175, 80);
|
||||
}
|
||||
.elements{
|
||||
display: flex;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
.hr{
|
||||
width: 100px;
|
||||
height: 4px;
|
||||
margin-top: 73px;
|
||||
background-image: linear-gradient(to right, #a9cc2e, #7cb732, #3a5d0d);
|
||||
}
|
||||
.logo_maze {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
flex-shrink: 0;
|
||||
|
||||
width: 113%;
|
||||
margin-left: -18px;
|
||||
margin-top: -207px
|
||||
}
|
||||
|
||||
.logo_tarj_end {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
flex-shrink: 0;
|
||||
|
||||
width: 113%;
|
||||
margin-left: -18px;
|
||||
margin-top: -57px
|
||||
}
|
||||
|
||||
.element-content{
|
||||
margin-top: 24px;
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
font-size: 24px;
|
||||
color: black;
|
||||
|
||||
}
|
||||
.paragonder{
|
||||
display: block;
|
||||
|
||||
width: 100%;
|
||||
height: calc(1.5em + .75rem + 2px);
|
||||
padding: .375rem .75rem;
|
||||
font-size: 1rem;
|
||||
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
||||
font-weight: 300;
|
||||
line-height: 1.5;
|
||||
color: #495057;
|
||||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: .25rem;
|
||||
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
|
||||
}
|
||||
.buton-transfer{
|
||||
border: none;
|
||||
width: 220px;
|
||||
color: #fff;
|
||||
background-image: linear-gradient(to right, #a9cc2e, #7cb732, #3a5d0d);
|
||||
padding: .5rem 1rem;
|
||||
font-size: 17px;
|
||||
line-height: 1.5;
|
||||
margin-top: 1.25rem;
|
||||
font-weight: 300;
|
||||
margin-bottom: .25rem;
|
||||
cursor: pointer;
|
||||
border-radius: 1.3rem;
|
||||
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.buton-cancel{
|
||||
border: none;
|
||||
width: 220px;
|
||||
color: #fff;
|
||||
background-image: linear-gradient(to right, #D3D3D3, #C5C5C5 , #B6B6B6);
|
||||
padding: .5rem 1rem;
|
||||
font-size: 17px;
|
||||
line-height: 1.5;
|
||||
margin-top: 1.25rem;
|
||||
font-weight: 300;
|
||||
margin-bottom: .25rem;
|
||||
cursor: pointer;
|
||||
border-radius: 1.3rem;
|
||||
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
|
||||
text-transform: none;
|
||||
}
|
||||
.select{
|
||||
border: 1px double #7cb732;
|
||||
}
|
||||
</style>
|
||||
152
HTML/gcphone/src/components/Bourse/Bourse.vue
Normal file
@@ -0,0 +1,152 @@
|
||||
<template>
|
||||
<div class="phone_app">
|
||||
<PhoneTitle :title="IntlString('APP_BOURSE_TITLE')" @back="onBackspace"/>
|
||||
<div class='elements'>
|
||||
<div class='element'
|
||||
v-for='(elem, key) in bourseInfo'
|
||||
v-bind:class="{ select: key === currentSelect}"
|
||||
v-bind:key="key">
|
||||
<div class="elem-evo"><i class="fa" :class="classInfo(elem)"></i></div>
|
||||
<div class="elem-libelle">{{elem.libelle}}</div>
|
||||
<div class="elem-price" :style="{color: colorBourse(elem)}">{{elem.price}} $ </div>
|
||||
<div class="elem-difference" :style="{color: colorBourse(elem)}"> <span v-if="elem.difference > 0">+</span>{{elem.difference}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import PhoneTitle from './../PhoneTitle'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PhoneTitle
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
currentSelect: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'useMouse', 'bourseInfo'])
|
||||
},
|
||||
methods: {
|
||||
scrollIntoViewIfNeeded: function () {
|
||||
this.$nextTick(() => {
|
||||
this.$el.querySelector('.select').scrollIntoViewIfNeeded()
|
||||
})
|
||||
},
|
||||
colorBourse (bouseItem) {
|
||||
if (bouseItem.difference === 0) {
|
||||
return '#007aff'
|
||||
} else if (bouseItem.difference < 0) {
|
||||
return '#2e7d32'
|
||||
} else {
|
||||
return '#c62828'
|
||||
}
|
||||
},
|
||||
classInfo (bouseItem) {
|
||||
if (bouseItem.difference === 0) {
|
||||
return ['fa-arrow-right', 'iblue']
|
||||
} else if (bouseItem.difference < 0) {
|
||||
return ['fa-arrow-up', 'ired']
|
||||
} else {
|
||||
return ['fa-arrow-down', 'igreen']
|
||||
}
|
||||
},
|
||||
onBackspace () {
|
||||
this.$router.push({ name: 'home' })
|
||||
},
|
||||
onUp () {
|
||||
this.currentSelect = this.currentSelect === 0 ? 0 : this.currentSelect - 1
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
onDown () {
|
||||
this.currentSelect = this.currentSelect === this.bourseInfo.length - 1 ? this.currentSelect : this.currentSelect + 1
|
||||
this.scrollIntoViewIfNeeded()
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$on('keyUpArrowUp', this.onUp)
|
||||
}
|
||||
this.$bus.$on('keyUpBackspace', this.onBackspace)
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$off('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$off('keyUpBackspace', this.onBackspace)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.screen{
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.title{
|
||||
padding-left: 16px;
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
background-color: rgb(76, 175, 80);
|
||||
}
|
||||
.elements{
|
||||
height: calc(100% - 34px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.element{
|
||||
height: 56px;
|
||||
width: 100%;
|
||||
line-height: 56px;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
.element.select{
|
||||
background-color: #DDD;
|
||||
}
|
||||
|
||||
.element .fa{
|
||||
color: #2e7d32;
|
||||
font-size: 18px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
.element .fa-arrow-down{
|
||||
color: #c62828;
|
||||
}
|
||||
.element .fa-arrow-right{
|
||||
color: #1565c0;
|
||||
}
|
||||
|
||||
.elem-libelle{
|
||||
padding-left: 6px;
|
||||
flex: 1;
|
||||
font-size: 22px;
|
||||
white-space: nowrap;
|
||||
font-weight: 100;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.elem-price{
|
||||
text-align: center;
|
||||
width: 90px;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
font-weight: 100;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.elem-difference{
|
||||
text-align: center;
|
||||
width: 60px;
|
||||
font-size: 14px;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
</style>
|
||||
35
HTML/gcphone/src/components/CurrentTime.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<span>{{time}}</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
time: '',
|
||||
myInterval: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateTime: function () {
|
||||
var time = new Date()
|
||||
var minutes = time.getMinutes()
|
||||
minutes = minutes > 9 ? minutes : '0' + minutes
|
||||
var heure = time.getHours()
|
||||
heure = heure > 9 ? heure : '0' + heure
|
||||
var datestring = heure + ':' + minutes
|
||||
this.time = datestring
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.updateTime()
|
||||
this.myInterval = setInterval(this.updateTime, 1000)
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
clearInterval(this.myInterval)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
236
HTML/gcphone/src/components/Home.vue
Normal file
@@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<div style="width: 326px; height: 743px;" class="home" v-bind:style="{background: 'url(' + backgroundURL +')'}">
|
||||
<InfoBare />
|
||||
<span class="warningMess" v-if="messages.length >= warningMessageCount">
|
||||
<div class="warningMess_icon"><i class="fa fa-warning"></i></div>
|
||||
<span class="warningMess_content">
|
||||
<span class="warningMess_title">{{ IntlString('PHONE_WARNING_MESSAGE') }}</span><br>
|
||||
<span class="warningMess_mess">{{messages.length}} / {{warningMessageCount}} {{IntlString('PHONE_WARNING_MESSAGE_MESS')}}</span>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<div class="time"></div>
|
||||
<div class="time-display">{{timeDisplay}}</div>
|
||||
|
||||
|
||||
<div class='home_buttons'>
|
||||
|
||||
<button style=" top: 73px; font-family:initial; margin-left: 10px; margin-right: 10px;"
|
||||
v-for="(but, key) of AppsHome"
|
||||
v-bind:key="but.name"
|
||||
v-bind:class="{ select: key === currentSelect}"
|
||||
v-bind:style="{backgroundImage: 'url(' + but.icons +')'}"
|
||||
|
||||
@click="openApp(but)"
|
||||
>
|
||||
<!--{{but.intlName}}-->
|
||||
<span class="puce" v-if="but.puce !== undefined && but.puce !== 0">{{but.puce}}</span>
|
||||
</button>
|
||||
|
||||
<div class="btn_menu_ctn">
|
||||
<button
|
||||
class="btn_menu"
|
||||
:class="{ select: AppsHome.length === currentSelect}"
|
||||
v-bind:style="{backgroundImage: 'url(' + '/html/static/img/icons_app/menu.png' +')'}"
|
||||
@click="openApp({routeName: 'menu'})"
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
import InfoBare from './InfoBare'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
InfoBare
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
currentSelect: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'useMouse', 'nbMessagesUnread', 'backgroundURL', 'messages', 'AppsHome', 'warningMessageCount'])
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['closePhone', 'setMessages']),
|
||||
onLeft () {
|
||||
this.currentSelect = (this.currentSelect + 1) % (this.AppsHome.length + 1)
|
||||
},
|
||||
onRight () {
|
||||
this.currentSelect = (this.currentSelect + this.AppsHome.length) % (this.AppsHome.length + 1)
|
||||
},
|
||||
onUp () {
|
||||
this.currentSelect = Math.max(this.currentSelect - 4, 0)
|
||||
},
|
||||
onDown () {
|
||||
this.currentSelect = Math.min(this.currentSelect + 4, this.AppsHome.length)
|
||||
},
|
||||
openApp (app) {
|
||||
this.$router.push({ name: app.routeName })
|
||||
},
|
||||
onEnter () {
|
||||
this.openApp(this.AppsHome[this.currentSelect] || {routeName: 'menu'})
|
||||
},
|
||||
onBack () {
|
||||
this.closePhone()
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpArrowLeft', this.onLeft)
|
||||
this.$bus.$on('keyUpArrowRight', this.onRight)
|
||||
this.$bus.$on('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$on('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
} else {
|
||||
this.currentSelect = -1
|
||||
}
|
||||
this.$bus.$on('keyUpBackspace', this.onBack)
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpArrowLeft', this.onLeft)
|
||||
this.$bus.$off('keyUpArrowRight', this.onRight)
|
||||
this.$bus.$off('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$off('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
this.$bus.$off('keyUpBackspace', this.onBack)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped="true">
|
||||
.home{
|
||||
background-size: cover !important;
|
||||
background-position: center !important;
|
||||
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
color: gray;
|
||||
}
|
||||
.warningMess{
|
||||
background-color: white;
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
right: 12px;
|
||||
top: 34px;
|
||||
min-height: 64px;
|
||||
display: flex;
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12);
|
||||
}
|
||||
.warningMess .warningMess_icon{
|
||||
display: flex;
|
||||
width: 16%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28px;
|
||||
height: 42px;
|
||||
width: 42px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.warningMess .warningMess_icon .fa {
|
||||
text-align: center;
|
||||
color: #F94B42;
|
||||
}
|
||||
.warningMess .warningMess_content{
|
||||
padding-left: 12px;
|
||||
background-color: rgba(255,255,255, 0.2);
|
||||
}
|
||||
.warningMess_title {
|
||||
font-size: 20px;
|
||||
}
|
||||
.warningMess_mess {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.home_buttons{
|
||||
display: flex;
|
||||
padding: 6px;
|
||||
width: 100%;
|
||||
bottom:1px;
|
||||
position: absolute;
|
||||
align-items: flex-end;
|
||||
flex-flow: row;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 3px;
|
||||
justify-content: space-between;
|
||||
transition: all 0.5s ease-in-out;
|
||||
}
|
||||
button{
|
||||
position: relative;
|
||||
margin: 0px;
|
||||
border: none;
|
||||
width: 80px;
|
||||
height: 76px;
|
||||
color: white;
|
||||
background-size: 64px 64px;
|
||||
background-position: center 6px;
|
||||
background-repeat: no-repeat;
|
||||
background-color: transparent;
|
||||
font-size: 14px;
|
||||
padding-top: 72px;
|
||||
font-weight: 700;
|
||||
text-shadow: -1px 0 0 rgba(0,0,0, 0.8),
|
||||
1px 0 0 rgba(0,0,0, 0.8),
|
||||
0 -1px 0 rgba(0,0,0, 0.8),
|
||||
0 1px 0 rgba(0,0,0, 0.8);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
button .puce{
|
||||
|
||||
position: absolute;
|
||||
display: block;
|
||||
background-color: #EE3838;
|
||||
font-size: 14px;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
top: -5px;
|
||||
left: 51px;
|
||||
font-family: none;
|
||||
line-height: 28px;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
font-weight: 400;
|
||||
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
||||
bottom: 32px;
|
||||
right: 12px;
|
||||
|
||||
bottom: 32px;
|
||||
right: 12px;
|
||||
}
|
||||
button.select, button:hover{
|
||||
background-color: rgba(255,255,255, 0.2);
|
||||
border-radius: 22%;
|
||||
}
|
||||
|
||||
.btn_menu_ctn{
|
||||
width: 100%;
|
||||
display: flex;
|
||||
height: 70px;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
border-radius: 24px;
|
||||
}
|
||||
.btn_menu {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
115
HTML/gcphone/src/components/InfoBare.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<div style="width: 326px; top: 4px;" class='phone_infoBare barre-header'>
|
||||
<span class='reseau'>{{ config.reseau }}</span>
|
||||
<span class="time">
|
||||
<current-time style="font-size: 12px; margin-right: 2px;"></current-time>
|
||||
</span>
|
||||
<hr class="batterie1">
|
||||
<hr class="batterie2">
|
||||
<hr class="barre1">
|
||||
<hr class="barre2">
|
||||
<hr class="barre3">
|
||||
<hr class="barre4">
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {mapGetters} from 'vuex'
|
||||
import CurrentTime from './CurrentTime'
|
||||
|
||||
export default {
|
||||
computed: mapGetters(['config']),
|
||||
components: {
|
||||
CurrentTime
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.barre-header {
|
||||
height: 24px;
|
||||
font-size: 17px;
|
||||
line-height: 24px;
|
||||
padding: 0px 20px 0px 24px;
|
||||
width: 100%;
|
||||
color: white;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.barre-header hr {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.reseau {
|
||||
font-size: 12px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.barre1 {
|
||||
height: 12px;
|
||||
width: 3px;
|
||||
right: 53px;
|
||||
background-color: rgba(255, 255, 255, 0.6);
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
border: none;
|
||||
bottom: -1px;
|
||||
}
|
||||
|
||||
.barre2 {
|
||||
height: 9px;
|
||||
width: 3px;
|
||||
right: 58px;
|
||||
background-color: white;
|
||||
border: none;
|
||||
bottom: -1px;
|
||||
}
|
||||
|
||||
.barre3 {
|
||||
height: 6px;
|
||||
width: 3px;
|
||||
right: 63px;
|
||||
background-color: white;
|
||||
border: none;
|
||||
bottom: -1px;
|
||||
}
|
||||
|
||||
.barre4 {
|
||||
height: 3px;
|
||||
width: 3px;
|
||||
right: 68px;
|
||||
background-color: white;
|
||||
border: none;
|
||||
bottom: -1px;
|
||||
}
|
||||
|
||||
.time {
|
||||
text-align: right;
|
||||
float: right;
|
||||
margin-right: -14px;
|
||||
font-size: 12px;
|
||||
padding-right: 12px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.batterie1 {
|
||||
height: 10px;
|
||||
width: 7px;
|
||||
right: 78px;
|
||||
background-color: rgb(255, 255, 255);
|
||||
color: rgb(255, 255, 255);
|
||||
border-radius: 0.5px;
|
||||
border: none;
|
||||
bottom: -1px;
|
||||
}
|
||||
|
||||
.batterie2 {
|
||||
height: 13px;
|
||||
width: 5px;
|
||||
right: 79px;
|
||||
bottom: 0px;
|
||||
background-color: rgba(255, 255, 255, 0.6);
|
||||
border: 0.5px solid white;
|
||||
border-radius: 1px;
|
||||
}
|
||||
</style>
|
||||
233
HTML/gcphone/src/components/List.vue
Normal file
@@ -0,0 +1,233 @@
|
||||
<template>
|
||||
<div class="phone_app">
|
||||
<PhoneTitle :title="title" :showInfoBare="showInfoBare" v-if="showHeader" @back="back"/>
|
||||
<!-- <InfoBare v-if="showInfoBare"/>
|
||||
<div v-if="title !== ''" class="phone_title" v-bind:style="styleTitle()">{{title}}</div>
|
||||
-->
|
||||
<div style="width: 324px; height: 595px;" class="phone_content elements">
|
||||
<div class="element" v-for='(elem, key) in list'
|
||||
v-bind:key="elem[keyDispay]"
|
||||
v-bind:class="{ select: key === currentSelect}"
|
||||
@click.stop="selectItem(elem)"
|
||||
@contextmenu.prevent="optionItem(elem)"
|
||||
>
|
||||
<div class="elem-pic" v-bind:style="stylePuce(elem)" @click.stop="selectItem(elem)">
|
||||
{{elem.letter || elem[keyDispay][0]}}
|
||||
</div>
|
||||
<div @click.stop="selectItem(elem)" v-if="elem.puce !== undefined && elem.puce !== 0" class="elem-puce">{{elem.puce}}</div>
|
||||
<div @click.stop="selectItem(elem)" v-if="elem.keyDesc === undefined || elem.keyDesc === ''" class="elem-title">{{elem[keyDispay]}}</div>
|
||||
<div @click.stop="selectItem(elem)" v-if="elem.keyDesc !== undefined && elem.keyDesc !== ''" class="elem-title-has-desc">{{elem[keyDispay]}}</div>
|
||||
<div @click.stop="selectItem(elem)" v-if="elem.keyDesc !== undefined && elem.keyDesc !== ''" class="elem-description">{{elem.keyDesc}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PhoneTitle from './PhoneTitle'
|
||||
import InfoBare from './InfoBare'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'hello',
|
||||
components: {
|
||||
PhoneTitle, InfoBare
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
currentSelect: 0
|
||||
}
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: 'Title'
|
||||
},
|
||||
showHeader: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showInfoBare: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
list: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#FFFFFF'
|
||||
},
|
||||
backgroundColor: {
|
||||
type: String,
|
||||
default: '#4CAF50'
|
||||
},
|
||||
keyDispay: {
|
||||
type: String,
|
||||
default: 'display'
|
||||
},
|
||||
disable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
titleBackgroundColor: {
|
||||
type: String,
|
||||
default: '#FFFFFF'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
list: function () {
|
||||
this.currentSelect = 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['useMouse'])
|
||||
},
|
||||
methods: {
|
||||
styleTitle: function () {
|
||||
return {
|
||||
color: this.color,
|
||||
backgroundColor: this.backgroundColor
|
||||
}
|
||||
},
|
||||
stylePuce (data) {
|
||||
data = data || {}
|
||||
if (data.icon !== undefined) {
|
||||
return {
|
||||
backgroundImage: `url(${data.icon})`,
|
||||
backgroundSize: 'cover',
|
||||
color: 'rgba(0,0,0,0)'
|
||||
}
|
||||
}
|
||||
return {
|
||||
color: data.color || this.color,
|
||||
backgroundColor: data.backgroundColor || this.backgroundColor,
|
||||
borderRadius: '50%'
|
||||
}
|
||||
},
|
||||
scrollIntoViewIfNeeded: function () {
|
||||
this.$nextTick(() => {
|
||||
document.querySelector('.select').scrollIntoViewIfNeeded()
|
||||
})
|
||||
},
|
||||
onUp: function () {
|
||||
if (this.disable === true) return
|
||||
this.currentSelect = this.currentSelect === 0 ? this.list.length - 1 : this.currentSelect - 1
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
onDown: function () {
|
||||
if (this.disable === true) return
|
||||
this.currentSelect = this.currentSelect === this.list.length - 1 ? 0 : this.currentSelect + 1
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
selectItem (item) {
|
||||
this.$emit('select', item)
|
||||
},
|
||||
optionItem (item) {
|
||||
this.$emit('option', item)
|
||||
},
|
||||
back () {
|
||||
this.$emit('back')
|
||||
},
|
||||
onRight: function () {
|
||||
if (this.disable === true) return
|
||||
this.$emit('option', this.list[this.currentSelect])
|
||||
},
|
||||
onEnter: function () {
|
||||
if (this.disable === true) return
|
||||
this.$emit('select', this.list[this.currentSelect])
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$on('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$on('keyUpArrowRight', this.onRight)
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
} else {
|
||||
this.currentSelect = -1
|
||||
}
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
this.$bus.$off('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$off('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$off('keyUpArrowRight', this.onRight)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.list{
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
.elements{
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.element{
|
||||
height: 58px;
|
||||
line-height: 58px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
font-weight: 300;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.element.select, .element:hover {
|
||||
background-color: #DDD;
|
||||
}
|
||||
|
||||
.elem-pic{
|
||||
margin-left: 12px;
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
text-align: center;
|
||||
line-height: 48px;
|
||||
font-weight: 200;
|
||||
}
|
||||
.elem-puce{
|
||||
background-color: #EE3838;
|
||||
top: 0px;
|
||||
color:white;
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
line-height: 18px;
|
||||
border-radius: 50%;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
position: absolute;
|
||||
left: 42px;
|
||||
z-index: 6;
|
||||
}
|
||||
.elem-title{
|
||||
margin-left: 12px;
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.elem-title-has-desc {
|
||||
margin-top:-15px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
.elem-description{
|
||||
text-align:left;
|
||||
color:grey;
|
||||
position:absolute;
|
||||
display:block;
|
||||
width:75%;
|
||||
left:73px;
|
||||
top:12px;
|
||||
font-weight: 100px;
|
||||
font-size:13.5px;
|
||||
font-style:italic;
|
||||
overflow:hidden;
|
||||
text-overflow:ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
219
HTML/gcphone/src/components/Menu.vue
Normal file
@@ -0,0 +1,219 @@
|
||||
<template>
|
||||
<div style="width: 326px; height: 743px;" class="phone_app">
|
||||
<div style="width: 342px;
|
||||
height: 756px;" class="backblur" v-bind:style="{background: 'url(' + backgroundURL +')'}"></div>
|
||||
<InfoBare class="infobare"/>
|
||||
<div class="menu" @click="onBack">
|
||||
|
||||
<div class="menu_content">
|
||||
|
||||
<div class='menu_buttons'>
|
||||
<button
|
||||
v-for="(but, key) of Apps"
|
||||
v-bind:key="but.name"
|
||||
|
||||
v-bind:class="{ select: key === currentSelect}"
|
||||
v-bind:style="{backgroundImage: 'url(' + but.icons +')'}"
|
||||
@click.stop="openApp(but)"
|
||||
>
|
||||
<span class="letra">{{but.intlName}}</span>
|
||||
<span class="puce" v-if="but.puce !== undefined && but.puce !== 0">{{but.puce}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import InfoBare from './InfoBare'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
InfoBare
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
currentSelect: 0,
|
||||
nBotonesMenu: 3
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['nbMessagesUnread', 'backgroundURL', 'Apps', 'useMouse'])
|
||||
},
|
||||
methods: {
|
||||
...mapGetters(['closePhone']),
|
||||
onLeft: function () {
|
||||
const l = Math.floor(this.currentSelect / this.nBotonesMenu)
|
||||
const newS = (this.currentSelect + this.nBotonesMenu - 1) % this.nBotonesMenu + l * this.nBotonesMenu
|
||||
this.currentSelect = Math.min(newS, this.Apps.length - 1)
|
||||
},
|
||||
onRight: function () {
|
||||
const l = Math.floor(this.currentSelect / this.nBotonesMenu)
|
||||
let newS = (this.currentSelect + 1) % this.nBotonesMenu + l * this.nBotonesMenu
|
||||
if (newS >= this.Apps.length) {
|
||||
newS = l * this.nBotonesMenu
|
||||
}
|
||||
this.currentSelect = newS
|
||||
},
|
||||
onUp: function () {
|
||||
let newS = this.currentSelect - this.nBotonesMenu
|
||||
if (newS < 0) {
|
||||
const r = this.currentSelect % this.nBotonesMenu
|
||||
newS = Math.floor((this.Apps.length - 1) / this.nBotonesMenu) * this.nBotonesMenu
|
||||
this.currentSelect = Math.min(newS + r, this.Apps.length - 1)
|
||||
} else {
|
||||
this.currentSelect = newS
|
||||
}
|
||||
},
|
||||
onDown: function () {
|
||||
const r = this.currentSelect % this.nBotonesMenu
|
||||
let newS = this.currentSelect + this.nBotonesMenu
|
||||
if (newS >= this.Apps.length) {
|
||||
newS = r
|
||||
}
|
||||
this.currentSelect = newS
|
||||
},
|
||||
openApp (app) {
|
||||
this.$router.push({ name: app.routeName })
|
||||
},
|
||||
onEnter () {
|
||||
this.openApp(this.Apps[this.currentSelect])
|
||||
},
|
||||
onBack: function () {
|
||||
this.$router.push({ name: 'home' })
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
},
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpArrowLeft', this.onLeft)
|
||||
this.$bus.$on('keyUpArrowRight', this.onRight)
|
||||
this.$bus.$on('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$on('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
} else {
|
||||
this.currentSelect = -1
|
||||
}
|
||||
this.$bus.$on('keyUpBackspace', this.onBack)
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpArrowLeft', this.onLeft)
|
||||
this.$bus.$off('keyUpArrowRight', this.onRight)
|
||||
this.$bus.$off('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$off('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
this.$bus.$off('keyUpBackspace', this.onBack)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.menu{
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
.backblur{
|
||||
top: -6px;
|
||||
left: -6px;
|
||||
right:-6px;
|
||||
bottom: -6px;
|
||||
position: absolute;
|
||||
background-size: cover !important;
|
||||
background-position: center !important;
|
||||
filter: blur(6px);
|
||||
}
|
||||
.menu_content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.menu_buttons{
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
margin-left: 10px;
|
||||
align-items: flex-start;
|
||||
align-content: flex-start;
|
||||
/* justify-content: space-around; */
|
||||
flex-flow: row;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 0px;
|
||||
|
||||
transition: all 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.menu_buttons {
|
||||
animation-name: up;
|
||||
animation-duration: 0.6s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
@keyframes up {
|
||||
from {transform: translateY(100vh);}
|
||||
to {transform: translateY(0);}
|
||||
}
|
||||
|
||||
|
||||
button{
|
||||
position: relative;
|
||||
margin: 0px;
|
||||
border: none;
|
||||
width: 80px;
|
||||
height: 110px;
|
||||
margin: 8px;
|
||||
color: white;
|
||||
background-size: 64px 64px;
|
||||
background-position: center 6px;
|
||||
background-repeat: no-repeat;
|
||||
background-color: transparent;
|
||||
font-size: 14px;
|
||||
padding-top: 72px;
|
||||
font-weight: 700;
|
||||
text-shadow: -1px 0 0 rgba(0,0,0, 0.8),
|
||||
1px 0 0 rgba(0,0,0, 0.8),
|
||||
0 -1px 0 rgba(0,0,0, 0.8),
|
||||
0 1px 0 rgba(0,0,0, 0.8);
|
||||
text-align: center;
|
||||
}
|
||||
button .puce{
|
||||
position: absolute;
|
||||
display: block;
|
||||
background-color: #EE3838;
|
||||
font-size: 14px;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
top: -5px;
|
||||
left: 51px;
|
||||
font-family: none;
|
||||
line-height: 28px;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
font-weight: 400;
|
||||
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
||||
bottom: 32px;
|
||||
right: 12px;
|
||||
|
||||
bottom: 32px;
|
||||
right: 12px;
|
||||
}
|
||||
button.select, button:hover{
|
||||
background-color: rgba(255,255,255, 0.2);
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.letra{
|
||||
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -10px);
|
||||
}
|
||||
|
||||
</style>
|
||||
150
HTML/gcphone/src/components/Modal/Modal.vue
Normal file
@@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<transition name="modal">
|
||||
<div
|
||||
class="modal-mask"
|
||||
@click.stop="cancel">
|
||||
|
||||
<div class="modal-container">
|
||||
<div class="modal-choix"
|
||||
v-bind:class="{ select: index === currentSelect}"
|
||||
v-for="(val, index) in choix" :key='index'
|
||||
v-bind:style="{color: val.color}"
|
||||
@click.stop="selectItem(val)"
|
||||
>
|
||||
<i @click.stop="selectItem(val)" class="fas" :class="val.icons" ></i>{{val.title}}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from './../../store'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'Modal',
|
||||
store: store,
|
||||
data () {
|
||||
return {
|
||||
currentSelect: 0
|
||||
}
|
||||
},
|
||||
props: {
|
||||
choix: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['useMouse'])
|
||||
},
|
||||
methods: {
|
||||
scrollIntoViewIfNeeded () {
|
||||
this.$nextTick(() => {
|
||||
document.querySelector('.modal-choix.select').scrollIntoViewIfNeeded()
|
||||
})
|
||||
},
|
||||
onUp () {
|
||||
this.currentSelect = this.currentSelect === 0 ? 0 : this.currentSelect - 1
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
onDown () {
|
||||
this.currentSelect = this.currentSelect === this.choix.length - 1 ? this.currentSelect : this.currentSelect + 1
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
selectItem (elem) {
|
||||
this.$emit('select', elem)
|
||||
},
|
||||
onEnter () {
|
||||
this.$emit('select', this.choix[this.currentSelect])
|
||||
},
|
||||
cancel () {
|
||||
this.$emit('cancel')
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$on('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
} else {
|
||||
this.currentSelect = -1
|
||||
}
|
||||
this.$bus.$on('keyUpBackspace', this.cancel)
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$off('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
this.$bus.$off('keyUpBackspace', this.cancel)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.modal-mask {
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 334px;
|
||||
height: 738px;
|
||||
background-color: rgba(0, 0, 0, .3);
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
transition: opacity .3s ease;
|
||||
}
|
||||
|
||||
.modal-container {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #fff;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, .33);
|
||||
transition: all .3s ease;
|
||||
padding-bottom: 16px;
|
||||
max-height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
text-align: center;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
color: #42B2DC;
|
||||
border-bottom: 2px solid #42B2DC;
|
||||
}
|
||||
.modal-choix {
|
||||
font-size: 15px;
|
||||
height: 56px;
|
||||
line-height: 56px;
|
||||
color: gray;
|
||||
position: relative;
|
||||
font-weight: 400;
|
||||
}
|
||||
.modal-choix .fa, .modal-choix .fas {
|
||||
font-size: 18px;
|
||||
line-height: 24px;
|
||||
margin-left: 12px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
.modal-choix .picto {
|
||||
z-index: 500;
|
||||
position: absolute;
|
||||
width: 42px;
|
||||
background-size: 100% !important;
|
||||
background-position-y: 100%;
|
||||
height: 42px;
|
||||
}
|
||||
.modal-choix.select, .modal-choix:hover {
|
||||
background-color: #E3E3E3;
|
||||
color: #0079d3
|
||||
}
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
179
HTML/gcphone/src/components/Modal/TextModal.vue
Normal file
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<transition name="modal">
|
||||
<div
|
||||
class="modal-mask"
|
||||
>
|
||||
<div class="modal-container" @click.stop >
|
||||
<h2 :style="{color}">{{ title }}</h2>
|
||||
<textarea
|
||||
class="modal-textarea"
|
||||
:class="{oneline: limit <= 18}"
|
||||
ref="textarea"
|
||||
:style="{borderColor: color}"
|
||||
v-model="inputText"
|
||||
:maxlength="limit"
|
||||
></textarea>
|
||||
<div class="botton-container">
|
||||
<button
|
||||
:style="{color}"
|
||||
@click="cancel"
|
||||
>
|
||||
{{ IntlString('CANCEL') }}
|
||||
</button>
|
||||
<button
|
||||
:style="{color}"
|
||||
@click="valide"
|
||||
>
|
||||
{{ IntlString('OK') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import store from './../../store'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'TextModal',
|
||||
store: store,
|
||||
data () {
|
||||
return {
|
||||
inputText: ''
|
||||
}
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
default: () => ''
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 255
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'themeColor']),
|
||||
color () {
|
||||
return this.themeColor || '#2A56C6'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
scrollIntoViewIfNeeded () {
|
||||
this.$nextTick(() => {
|
||||
document.querySelector('.modal-choix.select').scrollIntoViewIfNeeded()
|
||||
})
|
||||
},
|
||||
onUp () {
|
||||
this.currentSelect = this.currentSelect === 0 ? 0 : this.currentSelect - 1
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
onDown () {
|
||||
this.currentSelect = this.currentSelect === this.choix.length - 1 ? this.currentSelect : this.currentSelect + 1
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
selectItem (elem) {
|
||||
this.$emit('select', elem)
|
||||
},
|
||||
onEnter () {
|
||||
this.$emit('select', this.choix[this.currentSelect])
|
||||
},
|
||||
cancel () {
|
||||
this.$emit('cancel')
|
||||
},
|
||||
valide () {
|
||||
this.$emit('valid', {
|
||||
text: this.inputText
|
||||
})
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.inputText = this.text
|
||||
},
|
||||
mounted () {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.textarea.focus()
|
||||
})
|
||||
},
|
||||
beforeDestroy () {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.modal-mask {
|
||||
position: absolute;
|
||||
z-index: 99;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, .3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: opacity .3s ease;
|
||||
}
|
||||
|
||||
.modal-container {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #fff;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, .33);
|
||||
transition: all .3s ease;
|
||||
padding-bottom: 16px;
|
||||
max-height: 100%;
|
||||
width: 90%;
|
||||
overflow-y: auto;
|
||||
padding: 15px 20px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.modal-textarea {
|
||||
width: 100%;
|
||||
height: 140px;
|
||||
border: none;
|
||||
resize: none;
|
||||
border-bottom: 3px solid red;
|
||||
outline: none;
|
||||
font-size: 18px;
|
||||
}
|
||||
.modal-textarea.oneline {
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
|
||||
.botton-container {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.botton-container button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
padding: 6px 12px;
|
||||
outline: none;
|
||||
}
|
||||
.botton-container button:hover {
|
||||
background-color: rgba(0, 0, 0, .1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
49
HTML/gcphone/src/components/Modal/index.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import Vue from 'vue'
|
||||
import Modal from './Modal'
|
||||
import TextModal from './TextModal'
|
||||
import store from '@/store'
|
||||
import PhoneAPI from '@/PhoneAPI'
|
||||
|
||||
export default {
|
||||
CreateModal (propsData = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let modal = new (Vue.extend(Modal))({
|
||||
el: document.createElement('div'),
|
||||
propsData
|
||||
})
|
||||
document.querySelector('#app').appendChild(modal.$el)
|
||||
modal.$on('select', (data) => {
|
||||
resolve(data)
|
||||
modal.$el.parentNode.removeChild(modal.$el)
|
||||
modal.$destroy()
|
||||
})
|
||||
modal.$on('cancel', () => {
|
||||
resolve({title: 'cancel'})
|
||||
modal.$el.parentNode.removeChild(modal.$el)
|
||||
modal.$destroy()
|
||||
})
|
||||
})
|
||||
},
|
||||
CreateTextModal (propsData = {}) {
|
||||
if (store.getters.useMouse === false) {
|
||||
return PhoneAPI.getReponseText(propsData)
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
let modal = new (Vue.extend(TextModal))({
|
||||
el: document.createElement('div'),
|
||||
propsData
|
||||
})
|
||||
document.querySelector('#app').appendChild(modal.$el)
|
||||
modal.$on('valid', (data) => {
|
||||
resolve(data)
|
||||
modal.$el.parentNode.removeChild(modal.$el)
|
||||
modal.$destroy()
|
||||
})
|
||||
modal.$on('cancel', () => {
|
||||
reject('UserCancel')
|
||||
modal.$el.parentNode.removeChild(modal.$el)
|
||||
modal.$destroy()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
213
HTML/gcphone/src/components/Notes/NotesChannel.vue
Normal file
@@ -0,0 +1,213 @@
|
||||
<template>
|
||||
<div style="width: 334px; height: 742px; color: white" class="phone_app">
|
||||
<PhoneTitle :title="IntlString('APP_NOTES')" backgroundColor="#f8d344" color="white" @back="onBack" />
|
||||
<div style="backgroundColor: white;" class="elements" @contextmenu.prevent="addChannelOption">
|
||||
<div
|
||||
>
|
||||
<div v-for='(elem, key) in notesChannels'
|
||||
v-bind:key="elem.channel"
|
||||
v-bind:class="{ select: key === currentSelect}" class="elem-title">
|
||||
<h3 style="margin-left: 7px; font-size: 16px; font-weight: 400;"> {{elem.channel}}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
import Modal from '@/components/Modal/index.js'
|
||||
import PhoneTitle from './../PhoneTitle'
|
||||
|
||||
export default {
|
||||
components: { PhoneTitle },
|
||||
data: function () {
|
||||
return {
|
||||
currentSelect: 0,
|
||||
ignoreControls: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
list: function () {
|
||||
this.currentSelect = 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'useMouse', 'notesChannels', 'Apps'])
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['notesAddChannel', 'notesRemoveChannel']),
|
||||
scrollIntoViewIfNeeded () {
|
||||
this.$nextTick(() => {
|
||||
const $select = this.$el.querySelector('.select')
|
||||
if ($select !== null) {
|
||||
$select.scrollIntoViewIfNeeded()
|
||||
}
|
||||
})
|
||||
},
|
||||
onUp () {
|
||||
if (this.ignoreControls === true) return
|
||||
this.currentSelect = this.currentSelect === 0 ? 0 : this.currentSelect - 1
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
onDown () {
|
||||
if (this.ignoreControls === true) return
|
||||
this.currentSelect = this.currentSelect === this.notesChannels.length - 1 ? this.currentSelect : this.currentSelect + 1
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
async onRight () {
|
||||
if (this.ignoreControls === true) return
|
||||
this.ignoreControls = true
|
||||
let choix = [
|
||||
{id: 1, title: this.IntlString('APP_DARKTCHAT_NEW_NOTE'), icons: 'fa-plus', color: 'dodgerblue'},
|
||||
{id: 2, title: this.IntlString('APP_DARKTCHAT_DELETE_NOTE'), icons: 'fa-minus', color: 'tomato'},
|
||||
{id: 3, title: this.IntlString('APP_DARKTCHAT_CANCEL'), icons: 'fa-undo'}
|
||||
]
|
||||
if (this.notesChannels.length === 0) {
|
||||
choix.splice(1, 1)
|
||||
}
|
||||
const rep = await Modal.CreateModal({ choix })
|
||||
this.ignoreControls = false
|
||||
switch (rep.id) {
|
||||
case 1:
|
||||
this.addChannelOption()
|
||||
break
|
||||
case 2:
|
||||
this.removeChannelOption()
|
||||
break
|
||||
case 3 :
|
||||
}
|
||||
},
|
||||
async onEnter () {
|
||||
if (this.ignoreControls === true) return
|
||||
if (this.notesChannels.length === 0) {
|
||||
this.ignoreControls = true
|
||||
let choix = [
|
||||
{id: 1, title: this.IntlString('APP_DARKTCHAT_NEW_CHANNEL'), icons: 'fa-plus', color: 'green'},
|
||||
{id: 3, title: this.IntlString('APP_DARKTCHAT_CANCEL'), icons: 'fa-undo'}
|
||||
]
|
||||
const rep = await Modal.CreateModal({ choix })
|
||||
this.ignoreControls = false
|
||||
if (rep.id === 1) {
|
||||
this.addChannelOption()
|
||||
}
|
||||
} else {
|
||||
}
|
||||
},
|
||||
showChannel (channel) {
|
||||
this.$router.push({ name: 'notes.channel.show', params: { channel } })
|
||||
},
|
||||
onBack () {
|
||||
if (this.ignoreControls === true) return
|
||||
this.$router.push({ name: 'home' })
|
||||
},
|
||||
async addChannelOption () {
|
||||
try {
|
||||
const rep = await Modal.CreateTextModal({limit: 280, title: this.IntlString('APP_DARKTCHAT_NEW_CHANNEL')})
|
||||
let channel = (rep || {}).text || ' '
|
||||
channel
|
||||
if (channel.length > 0) {
|
||||
this.currentSelect = 0
|
||||
this.notesAddChannel({ channel })
|
||||
}
|
||||
} catch (e) {}
|
||||
},
|
||||
async removeChannelOption () {
|
||||
const channel = this.notesChannels[this.currentSelect].channel
|
||||
this.currentSelect = 0
|
||||
this.notesRemoveChannel({ channel })
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$on('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$on('keyUpArrowRight', this.onRight)
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
this.$bus.$on('keyUpBackspace', this.onBack)
|
||||
} else {
|
||||
this.currentSelect = -1
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$off('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$off('keyUpArrowRight', this.onRight)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
this.$bus.$off('keyUpBackspace', this.onBack)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.list{
|
||||
height: 100%;
|
||||
}
|
||||
.title{
|
||||
padding-top: 22px;
|
||||
padding-left: 16px;
|
||||
height: 54px;
|
||||
line-height: 34px;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.elements{
|
||||
height: calc(100% - 54px);
|
||||
overflow-y: auto;
|
||||
background-color: #20201d;
|
||||
color: #34302f
|
||||
}
|
||||
.element{
|
||||
margin-top: 50px;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.elem-title{
|
||||
margin-left: 6px;
|
||||
width: 300px;
|
||||
font-size: 20px;
|
||||
transition: .15s;
|
||||
font-weight: 200;
|
||||
color: #34302f;
|
||||
margin-left: 13px;
|
||||
border-radius: 13px;
|
||||
|
||||
}
|
||||
.elem-title .diese {
|
||||
color: #34302f;
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.elem-title.select, .elem-title:hover{
|
||||
background-color:rgba(112, 108, 108, 0.1);
|
||||
color: #34302f;
|
||||
|
||||
}
|
||||
.element.select .elem-title, .element:hover .elem-title {
|
||||
margin-left: 12px;
|
||||
}
|
||||
.element.select .elem-title .diese, .element:hover .elem-title .diese {
|
||||
color:#f8d344;
|
||||
}
|
||||
.elements::-webkit-scrollbar-track
|
||||
{
|
||||
box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
.elements::-webkit-scrollbar
|
||||
{
|
||||
width: 3px;
|
||||
background-color: transparent;
|
||||
}
|
||||
.elements::-webkit-scrollbar-thumb
|
||||
{
|
||||
background-color: white;
|
||||
}
|
||||
</style>
|
||||
217
HTML/gcphone/src/components/Notes/NotesMessage.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<div style="width: 334px; height: 742px; background: white" class="phone_app">
|
||||
<PhoneTitle :title="channelName" backgroundColor="#f8d344" @back="onQuit"/>
|
||||
<div class="phone_content">
|
||||
<div class="elements" ref="elementsDiv">
|
||||
<div class="element" v-for='(elem) in notesMessages'
|
||||
v-bind:key="elem.id"
|
||||
>
|
||||
<div class="time">{{formatTime(elem.time)}}</div>
|
||||
<div class="message">
|
||||
{{elem.message}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='notes_write'>
|
||||
<input type="text" placeholder="..." v-model="message" @keyup.enter.prevent="sendMessage">
|
||||
<span class='notes_send' @click="sendMessage">></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
import PhoneTitle from './../PhoneTitle'
|
||||
|
||||
export default {
|
||||
components: { PhoneTitle },
|
||||
data () {
|
||||
return {
|
||||
message: '',
|
||||
channel: '',
|
||||
currentSelect: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['notesMessages', 'notesCurrentChannel', 'useMouse']),
|
||||
channelName () {
|
||||
return '# ' + this.channel
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
notesMessages () {
|
||||
const c = this.$refs.elementsDiv
|
||||
c.scrollTop = c.scrollHeight
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setChannel (channel) {
|
||||
this.channel = channel
|
||||
this.notesSetChannel({ channel })
|
||||
},
|
||||
...mapActions(['notesSetChannel', 'notesSendMessage']),
|
||||
scrollIntoViewIfNeeded () {
|
||||
this.$nextTick(() => {
|
||||
const $select = this.$el.querySelector('.select')
|
||||
if ($select !== null) {
|
||||
$select.scrollIntoViewIfNeeded()
|
||||
}
|
||||
})
|
||||
},
|
||||
onUp () {
|
||||
const c = this.$refs.elementsDiv
|
||||
c.scrollTop = c.scrollTop - 120
|
||||
},
|
||||
onDown () {
|
||||
const c = this.$refs.elementsDiv
|
||||
c.scrollTop = c.scrollTop + 120
|
||||
},
|
||||
async onEnter () {
|
||||
const rep = await this.$phoneAPI.getReponseText()
|
||||
if (rep !== undefined && rep.text !== undefined) {
|
||||
const message = rep.text.trim()
|
||||
if (message.length !== 0) {
|
||||
this.notesSendMessage({
|
||||
channel: this.channel,
|
||||
message
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
sendMessage () {
|
||||
const message = this.message.trim()
|
||||
if (message.length !== 0) {
|
||||
this.notesSendMessage({
|
||||
channel: this.channel,
|
||||
message
|
||||
})
|
||||
this.message = ''
|
||||
}
|
||||
},
|
||||
onBack () {
|
||||
if (this.useMouse === true && document.activeElement.tagName !== 'BODY') return
|
||||
this.onQuit()
|
||||
},
|
||||
onQuit () {
|
||||
this.$router.push({ name: 'notes.channel' })
|
||||
},
|
||||
formatTime (time) {
|
||||
const d = new Date(time)
|
||||
return d.toLocaleTimeString()
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$on('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
} else {
|
||||
this.currentSelect = -1
|
||||
}
|
||||
this.$bus.$on('keyUpBackspace', this.onBack)
|
||||
this.setChannel(this.$route.params.channel)
|
||||
},
|
||||
mounted () {
|
||||
window.c = this.$refs.elementsDiv
|
||||
const c = this.$refs.elementsDiv
|
||||
c.scrollTop = c.scrollHeight
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$off('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
this.$bus.$off('keyUpBackspace', this.onBack)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.elements{
|
||||
height: calc(100% - 56px);
|
||||
background-color: #dae0e6;
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 12px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.element{
|
||||
color: #a6a28c;
|
||||
flex: 0 0 auto;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
/* margin: 9px 12px;
|
||||
line-height: 18px;
|
||||
font-size: 18px;
|
||||
padding-bottom: 6px;
|
||||
|
||||
flex-direction: row;
|
||||
height: 60px; */
|
||||
}
|
||||
|
||||
.time{
|
||||
padding-right: 10px;
|
||||
font-size: 10px;
|
||||
margin-left: 15px;
|
||||
|
||||
}
|
||||
|
||||
.message{
|
||||
width: 100%;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.notes_write{
|
||||
height: 56px;
|
||||
widows: 100%;
|
||||
background: #dae0e6;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
.notes_write input{
|
||||
width: 75%;
|
||||
margin-left: 6%;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: 16px;
|
||||
padding: 3px 5px;
|
||||
float: left;
|
||||
height: 36px;
|
||||
background-color: white;
|
||||
color: black;
|
||||
}
|
||||
.notes_write input::placeholder {
|
||||
color: #ccc;
|
||||
}
|
||||
.notes_send{
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
float: right;
|
||||
border-radius: 50%;
|
||||
background-color: #f8d344;
|
||||
margin-right: 12px;
|
||||
margin-bottom: 2px;
|
||||
color: white;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
.elements::-webkit-scrollbar-track
|
||||
{
|
||||
box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
||||
background-color: #a6a28c;
|
||||
}
|
||||
.elements::-webkit-scrollbar
|
||||
{
|
||||
width: 3px;
|
||||
background-color: transparent;
|
||||
}
|
||||
.elements::-webkit-scrollbar-thumb
|
||||
{
|
||||
background-color: #FFC629;
|
||||
}
|
||||
</style>
|
||||
48
HTML/gcphone/src/components/PhoneTitle.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div class="phone_title_content" :style="style" :class="{'hasInfoBare': showInfoBare}" >
|
||||
<InfoBare v-if="showInfoBare" />
|
||||
<div class="phone_title" :style="{backgroundColor: backgroundColor}">
|
||||
<button class="btn-back" @click.stop="back"><i class="fas fa-angle-left" @click.stop="back"></i></button>
|
||||
{{title}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import InfoBare from './InfoBare'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
InfoBare
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['themeColorTitle']),
|
||||
style () {
|
||||
return {
|
||||
backgroundColor: this.backgroundColor || this.themeColorTitle,
|
||||
color: this.color || '#FFF'
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
back () {
|
||||
this.$emit('back')
|
||||
}
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
showInfoBare: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
backgroundColor: {
|
||||
type: String
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
</style>
|
||||
8
HTML/gcphone/src/components/Photo/Photo.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<script>
|
||||
import PhoneAPI from './../../PhoneAPI'
|
||||
export default {
|
||||
created () {
|
||||
PhoneAPI.faketakePhoto()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
209
HTML/gcphone/src/components/Tchat/TchatChannel.vue
Normal file
@@ -0,0 +1,209 @@
|
||||
<template>
|
||||
<div style="width: 334px; height: 742px; color: white" class="phone_app">
|
||||
<PhoneTitle style="color: white" :title="IntlString('APP_DARKTCHAT_TITLE')" backgroundColor="#ff4500" @back="onBack" />
|
||||
<div style="backgroundColor: #dae0e6;" class="elements" @contextmenu.prevent="addChannelOption">
|
||||
<div class="element" v-for='(elem, key) in tchatChannels'
|
||||
v-bind:key="elem.channel"
|
||||
v-bind:class="{ select: key === currentSelect}"
|
||||
@click.stop="showChannel(elem.channel)"
|
||||
>
|
||||
<div class="elem-title" @click.stop="showChannel(elem.channel)"><span class="diese">#</span> {{elem.channel}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
import Modal from '@/components/Modal/index.js'
|
||||
import PhoneTitle from './../PhoneTitle'
|
||||
|
||||
export default {
|
||||
components: { PhoneTitle },
|
||||
data: function () {
|
||||
return {
|
||||
currentSelect: 0,
|
||||
ignoreControls: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
list: function () {
|
||||
this.currentSelect = 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'useMouse', 'tchatChannels', 'Apps'])
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['tchatAddChannel', 'tchatRemoveChannel']),
|
||||
scrollIntoViewIfNeeded () {
|
||||
this.$nextTick(() => {
|
||||
const $select = this.$el.querySelector('.select')
|
||||
if ($select !== null) {
|
||||
$select.scrollIntoViewIfNeeded()
|
||||
}
|
||||
})
|
||||
},
|
||||
onUp () {
|
||||
if (this.ignoreControls === true) return
|
||||
this.currentSelect = this.currentSelect === 0 ? 0 : this.currentSelect - 1
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
onDown () {
|
||||
if (this.ignoreControls === true) return
|
||||
this.currentSelect = this.currentSelect === this.tchatChannels.length - 1 ? this.currentSelect : this.currentSelect + 1
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
async onRight () {
|
||||
if (this.ignoreControls === true) return
|
||||
this.ignoreControls = true
|
||||
let choix = [
|
||||
{id: 1, title: this.IntlString('APP_DARKTCHAT_NEW_CHANNEL'), icons: 'fa-plus', color: 'dodgerblue'},
|
||||
{id: 2, title: this.IntlString('APP_DARKTCHAT_DELETE_CHANNEL'), icons: 'fa-minus', color: 'tomato'},
|
||||
{id: 3, title: this.IntlString('APP_DARKTCHAT_CANCEL'), icons: 'fa-undo'}
|
||||
]
|
||||
if (this.tchatChannels.length === 0) {
|
||||
choix.splice(1, 1)
|
||||
}
|
||||
const rep = await Modal.CreateModal({ choix })
|
||||
this.ignoreControls = false
|
||||
switch (rep.id) {
|
||||
case 1:
|
||||
this.addChannelOption()
|
||||
break
|
||||
case 2:
|
||||
this.removeChannelOption()
|
||||
break
|
||||
case 3 :
|
||||
}
|
||||
},
|
||||
async onEnter () {
|
||||
if (this.ignoreControls === true) return
|
||||
if (this.tchatChannels.length === 0) {
|
||||
this.ignoreControls = true
|
||||
let choix = [
|
||||
{id: 1, title: this.IntlString('APP_DARKTCHAT_NEW_CHANNEL'), icons: 'fa-plus', color: 'green'},
|
||||
{id: 3, title: this.IntlString('APP_DARKTCHAT_CANCEL'), icons: 'fa-undo'}
|
||||
]
|
||||
const rep = await Modal.CreateModal({ choix })
|
||||
this.ignoreControls = false
|
||||
if (rep.id === 1) {
|
||||
this.addChannelOption()
|
||||
}
|
||||
} else {
|
||||
const channel = this.tchatChannels[this.currentSelect].channel
|
||||
this.showChannel(channel)
|
||||
}
|
||||
},
|
||||
showChannel (channel) {
|
||||
this.$router.push({ name: 'tchat.channel.show', params: { channel } })
|
||||
},
|
||||
onBack () {
|
||||
if (this.ignoreControls === true) return
|
||||
this.$router.push({ name: 'home' })
|
||||
},
|
||||
async addChannelOption () {
|
||||
try {
|
||||
const rep = await Modal.CreateTextModal({limit: 20, title: this.IntlString('APP_DARKTCHAT_NEW_CHANNEL')})
|
||||
let channel = (rep || {}).text || ''
|
||||
channel = channel.toLowerCase().replace(/[^a-z]/g, '')
|
||||
if (channel.length > 0) {
|
||||
this.currentSelect = 0
|
||||
this.tchatAddChannel({ channel })
|
||||
}
|
||||
} catch (e) {}
|
||||
},
|
||||
async removeChannelOption () {
|
||||
const channel = this.tchatChannels[this.currentSelect].channel
|
||||
this.currentSelect = 0
|
||||
this.tchatRemoveChannel({ channel })
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$on('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$on('keyUpArrowRight', this.onRight)
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
this.$bus.$on('keyUpBackspace', this.onBack)
|
||||
} else {
|
||||
this.currentSelect = -1
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$off('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$off('keyUpArrowRight', this.onRight)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
this.$bus.$off('keyUpBackspace', this.onBack)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.list{
|
||||
height: 100%;
|
||||
}
|
||||
.title{
|
||||
padding-top: 22px;
|
||||
padding-left: 16px;
|
||||
height: 54px;
|
||||
line-height: 34px;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.elements{
|
||||
height: calc(100% - 54px);
|
||||
overflow-y: auto;
|
||||
background-color: #20201d;
|
||||
color: #a6a28c
|
||||
}
|
||||
.element{
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.elem-title{
|
||||
margin-left: 6px;
|
||||
font-size: 20px;
|
||||
text-transform: capitalize;
|
||||
transition: .15s;
|
||||
font-weight: 400;
|
||||
}
|
||||
.elem-title .diese {
|
||||
color: #0079d3;
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.element.select, .element:hover{
|
||||
background-color: white;
|
||||
color: #0079d3;
|
||||
|
||||
}
|
||||
.element.select .elem-title, .element:hover .elem-title {
|
||||
margin-left: 12px;
|
||||
}
|
||||
.element.select .elem-title .diese, .element:hover .elem-title .diese {
|
||||
color:#0079d3;
|
||||
}
|
||||
.elements::-webkit-scrollbar-track
|
||||
{
|
||||
box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
.elements::-webkit-scrollbar
|
||||
{
|
||||
width: 3px;
|
||||
background-color: transparent;
|
||||
}
|
||||
.elements::-webkit-scrollbar-thumb
|
||||
{
|
||||
background-color: #0079d3;
|
||||
}
|
||||
</style>
|
||||
217
HTML/gcphone/src/components/Tchat/TchatMessage.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<div style="width: 334px; height: 742px; background: white" class="phone_app">
|
||||
<PhoneTitle :title="channelName" backgroundColor="#ff4500" @back="onQuit"/>
|
||||
<div class="phone_content">
|
||||
<div class="elements" ref="elementsDiv">
|
||||
<div class="element" v-for='(elem) in tchatMessages'
|
||||
v-bind:key="elem.id"
|
||||
>
|
||||
<div class="time">{{formatTime(elem.time)}}</div>
|
||||
<div class="message">
|
||||
{{elem.message}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='tchat_write'>
|
||||
<input type="text" placeholder="..." v-model="message" @keyup.enter.prevent="sendMessage">
|
||||
<span class='tchat_send' @click="sendMessage">></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
import PhoneTitle from './../PhoneTitle'
|
||||
|
||||
export default {
|
||||
components: { PhoneTitle },
|
||||
data () {
|
||||
return {
|
||||
message: '',
|
||||
channel: '',
|
||||
currentSelect: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['tchatMessages', 'tchatCurrentChannel', 'useMouse']),
|
||||
channelName () {
|
||||
return '# ' + this.channel
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
tchatMessages () {
|
||||
const c = this.$refs.elementsDiv
|
||||
c.scrollTop = c.scrollHeight
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setChannel (channel) {
|
||||
this.channel = channel
|
||||
this.tchatSetChannel({ channel })
|
||||
},
|
||||
...mapActions(['tchatSetChannel', 'tchatSendMessage']),
|
||||
scrollIntoViewIfNeeded () {
|
||||
this.$nextTick(() => {
|
||||
const $select = this.$el.querySelector('.select')
|
||||
if ($select !== null) {
|
||||
$select.scrollIntoViewIfNeeded()
|
||||
}
|
||||
})
|
||||
},
|
||||
onUp () {
|
||||
const c = this.$refs.elementsDiv
|
||||
c.scrollTop = c.scrollTop - 120
|
||||
},
|
||||
onDown () {
|
||||
const c = this.$refs.elementsDiv
|
||||
c.scrollTop = c.scrollTop + 120
|
||||
},
|
||||
async onEnter () {
|
||||
const rep = await this.$phoneAPI.getReponseText()
|
||||
if (rep !== undefined && rep.text !== undefined) {
|
||||
const message = rep.text.trim()
|
||||
if (message.length !== 0) {
|
||||
this.tchatSendMessage({
|
||||
channel: this.channel,
|
||||
message
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
sendMessage () {
|
||||
const message = this.message.trim()
|
||||
if (message.length !== 0) {
|
||||
this.tchatSendMessage({
|
||||
channel: this.channel,
|
||||
message
|
||||
})
|
||||
this.message = ''
|
||||
}
|
||||
},
|
||||
onBack () {
|
||||
if (this.useMouse === true && document.activeElement.tagName !== 'BODY') return
|
||||
this.onQuit()
|
||||
},
|
||||
onQuit () {
|
||||
this.$router.push({ name: 'tchat.channel' })
|
||||
},
|
||||
formatTime (time) {
|
||||
const d = new Date(time)
|
||||
return d.toLocaleTimeString()
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$on('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
} else {
|
||||
this.currentSelect = -1
|
||||
}
|
||||
this.$bus.$on('keyUpBackspace', this.onBack)
|
||||
this.setChannel(this.$route.params.channel)
|
||||
},
|
||||
mounted () {
|
||||
window.c = this.$refs.elementsDiv
|
||||
const c = this.$refs.elementsDiv
|
||||
c.scrollTop = c.scrollHeight
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$off('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
this.$bus.$off('keyUpBackspace', this.onBack)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.elements{
|
||||
height: calc(100% - 56px);
|
||||
background-color: #dae0e6;
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 12px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.element{
|
||||
color: #a6a28c;
|
||||
flex: 0 0 auto;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
/* margin: 9px 12px;
|
||||
line-height: 18px;
|
||||
font-size: 18px;
|
||||
padding-bottom: 6px;
|
||||
|
||||
flex-direction: row;
|
||||
height: 60px; */
|
||||
}
|
||||
|
||||
.time{
|
||||
padding-right: 10px;
|
||||
font-size: 10px;
|
||||
margin-left: 15px;
|
||||
|
||||
}
|
||||
|
||||
.message{
|
||||
width: 100%;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.tchat_write{
|
||||
height: 56px;
|
||||
widows: 100%;
|
||||
background: #dae0e6;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
.tchat_write input{
|
||||
width: 75%;
|
||||
margin-left: 6%;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: 16px;
|
||||
padding: 3px 5px;
|
||||
float: left;
|
||||
height: 36px;
|
||||
background-color: white;
|
||||
color: black;
|
||||
}
|
||||
.tchat_write input::placeholder {
|
||||
color: #ccc;
|
||||
}
|
||||
.tchat_send{
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
float: right;
|
||||
border-radius: 50%;
|
||||
background-color: #ff4500;
|
||||
margin-right: 12px;
|
||||
margin-bottom: 2px;
|
||||
color: white;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
.elements::-webkit-scrollbar-track
|
||||
{
|
||||
box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
||||
background-color: #a6a28c;
|
||||
}
|
||||
.elements::-webkit-scrollbar
|
||||
{
|
||||
width: 3px;
|
||||
background-color: transparent;
|
||||
}
|
||||
.elements::-webkit-scrollbar-thumb
|
||||
{
|
||||
background-color: #FFC629;
|
||||
}
|
||||
</style>
|
||||
36
HTML/gcphone/src/components/Tchat/TchatSplashScreen.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div style="width: 334px; height: 678px; background: white" class="splash">
|
||||
<img src="/html/static/img/app_tchat/reddit.png" alt="">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
created: function () {
|
||||
setTimeout(() => {
|
||||
this.$router.push({ name: 'tchat.channel' })
|
||||
}, 700)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.splash{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #20201d;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
img {
|
||||
width: 80px;
|
||||
animation-name: zoom;
|
||||
animation-duration: 0.7s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
@keyframes zoom {
|
||||
from {width: 80px;}
|
||||
to {width: 250px;}
|
||||
}
|
||||
</style>
|
||||
346
HTML/gcphone/src/components/contacts/Contact.vue
Normal file
@@ -0,0 +1,346 @@
|
||||
<template>
|
||||
<div style="width: 326px; height: 743px;" class="phone_app">
|
||||
<PhoneTitle :title="contact.display" @back="forceCancel"/>
|
||||
<div class='phone_content content inputText'>
|
||||
|
||||
<div class="group select" data-type="text" data-model='display' data-maxlength = '64'>
|
||||
<input type="text" v-model="contact.display" maxlength="64" v-autofocus>
|
||||
<span class="highlight"></span>
|
||||
<span class="bar"></span>
|
||||
<label>{{ IntlString('APP_CONTACT_LABEL_NAME') }}</label>
|
||||
</div>
|
||||
|
||||
<div class="group inputText" data-type="text" data-model='number' data-maxlength='10'>
|
||||
<input type="text" v-model="contact.number" maxlength="10">
|
||||
<span class="highlight"></span>
|
||||
<span class="bar"></span>
|
||||
<label>{{ IntlString('APP_CONTACT_LABEL_NUMBER') }}</label>
|
||||
</div>
|
||||
<div style="margin-top: 23px; width: 263px; margin-left: 23px; " class="group " data-type="button" data-action='save' @click.stop="save">
|
||||
<input style="font-weight: 100;" type='button' class="btn btn-green" :value="IntlString('APP_CONTACT_SAVE')" @click.stop="save"/>
|
||||
</div>
|
||||
<div style="margin-top: 23px; width: 263px; margin-left: 23px;" class="group" data-type="button" data-action='cancel' @click.stop="forceCancel">
|
||||
<input style="font-weight: 100;" type='button' class="btn btn-orange" :value="IntlString('APP_CONTACT_CANCEL')" @click.stop="forceCancel"/>
|
||||
</div>
|
||||
<div style="margin-top: 23px; width: 263px; margin-left: 23px;" class="group" data-type="button" data-action='deleteC' @click.stop="deleteC">
|
||||
<input style="font-weight: 100;" type='button' class="btn btn-red" :value="IntlString('APP_CONTACT_DELETE')" @click.stop="deleteC"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
import PhoneTitle from './../PhoneTitle'
|
||||
import Modal from '@/components/Modal/index.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PhoneTitle
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
id: -1,
|
||||
currentSelect: 0,
|
||||
ignoreControls: false,
|
||||
contact: {
|
||||
display: '',
|
||||
number: '',
|
||||
id: -1
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'contacts', 'useMouse'])
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['updateContact', 'addContact']),
|
||||
onUp () {
|
||||
if (this.ignoreControls === true) return
|
||||
let select = document.querySelector('.group.select')
|
||||
if (select.previousElementSibling !== null) {
|
||||
document.querySelectorAll('.group').forEach(elem => {
|
||||
elem.classList.remove('select')
|
||||
})
|
||||
select.previousElementSibling.classList.add('select')
|
||||
let i = select.previousElementSibling.querySelector('input')
|
||||
if (i !== null) {
|
||||
i.focus()
|
||||
}
|
||||
}
|
||||
},
|
||||
onDown () {
|
||||
if (this.ignoreControls === true) return
|
||||
let select = document.querySelector('.group.select')
|
||||
if (select.nextElementSibling !== null) {
|
||||
document.querySelectorAll('.group').forEach(elem => {
|
||||
elem.classList.remove('select')
|
||||
})
|
||||
select.nextElementSibling.classList.add('select')
|
||||
let i = select.nextElementSibling.querySelector('input')
|
||||
if (i !== null) {
|
||||
i.focus()
|
||||
}
|
||||
}
|
||||
},
|
||||
onEnter () {
|
||||
if (this.ignoreControls === true) return
|
||||
let select = document.querySelector('.group.select')
|
||||
if (select.dataset.type === 'text') {
|
||||
let options = {
|
||||
limit: parseInt(select.dataset.maxlength) || 64,
|
||||
text: this.contact[select.dataset.model] || ''
|
||||
}
|
||||
this.$phoneAPI.getReponseText(options).then(data => {
|
||||
this.contact[select.dataset.model] = data.text
|
||||
})
|
||||
}
|
||||
if (select.dataset.action && this[select.dataset.action]) {
|
||||
this[select.dataset.action]()
|
||||
}
|
||||
},
|
||||
save () {
|
||||
if (this.id === -1 || this.id === 0) {
|
||||
this.addContact({
|
||||
display: this.contact.display,
|
||||
number: this.contact.number
|
||||
})
|
||||
} else {
|
||||
this.updateContact({
|
||||
id: this.id,
|
||||
display: this.contact.display,
|
||||
number: this.contact.number
|
||||
})
|
||||
}
|
||||
history.back()
|
||||
},
|
||||
cancel () {
|
||||
if (this.ignoreControls === true) return
|
||||
if (this.useMouse === true && document.activeElement.tagName !== 'BODY') return
|
||||
history.back()
|
||||
},
|
||||
forceCancel () {
|
||||
history.back()
|
||||
},
|
||||
deleteC () {
|
||||
if (this.id !== -1) {
|
||||
this.ignoreControls = true
|
||||
let choix = [{title: 'Abbrechen'}, {title: 'Löschen', color: 'red'}]
|
||||
Modal.CreateModal({choix}).then(reponse => {
|
||||
this.ignoreControls = false
|
||||
if (reponse.title === 'Löschen') {
|
||||
this.$phoneAPI.deleteContact(this.id)
|
||||
history.back()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
history.back()
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$on('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
} else {
|
||||
this.currentSelect = -1
|
||||
}
|
||||
this.$bus.$on('keyUpBackspace', this.cancel)
|
||||
this.id = parseInt(this.$route.params.id)
|
||||
this.contact.display = this.IntlString('APP_CONTACT_NEW')
|
||||
this.contact.number = this.$route.params.number
|
||||
if (this.id !== -1) {
|
||||
const c = this.contacts.find(e => e.id === this.id)
|
||||
if (c !== undefined) {
|
||||
this.contact = {
|
||||
id: c.id,
|
||||
display: c.display,
|
||||
number: c.number
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
this.$bus.$off('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$off('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
this.$bus.$off('keyUpBackspace', this.cancel)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.contact{
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.title{
|
||||
padding-left: 16px;
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
font-weight: 700;
|
||||
background-color: #5264AE;
|
||||
color: white;
|
||||
}
|
||||
.content{
|
||||
margin: 6px 10px;
|
||||
margin-top: 28px;
|
||||
}
|
||||
.group {
|
||||
position:relative;
|
||||
margin-top:24px;
|
||||
}
|
||||
.group.inputText {
|
||||
position:relative;
|
||||
margin-top:45px;
|
||||
}
|
||||
input {
|
||||
font-size:24px;
|
||||
display:block;
|
||||
width:100%;
|
||||
border:none;
|
||||
border-bottom:1px solid #e9e9eb;
|
||||
font-weight: 100;
|
||||
font-size: 20px;
|
||||
}
|
||||
input:focus { outline:none; }
|
||||
|
||||
/* LABEL ======================================= */
|
||||
label {
|
||||
color:#999;
|
||||
font-size:18px;
|
||||
font-weight:normal;
|
||||
position:absolute;
|
||||
pointer-events:none;
|
||||
left:5px;
|
||||
top:10px;
|
||||
transition:0.2s ease all;
|
||||
-moz-transition:0.2s ease all;
|
||||
-webkit-transition:0.2s ease all;
|
||||
}
|
||||
|
||||
/* active state */
|
||||
input:focus ~ label, input:valid ~ label {
|
||||
top:-24px;
|
||||
font-size:18px;
|
||||
color:gray;
|
||||
}
|
||||
|
||||
/* BOTTOM BARS ================================= */
|
||||
.bar { position:relative; display:block; width:100%; }
|
||||
.bar:before, .bar:after {
|
||||
content:'';
|
||||
height:3px;
|
||||
width:0;
|
||||
bottom:1px;
|
||||
position:absolute;
|
||||
transition:0.2s ease all;
|
||||
-moz-transition:0.2s ease all;
|
||||
-webkit-transition:0.2s ease all;
|
||||
}
|
||||
.bar:before {
|
||||
left:50%;
|
||||
}
|
||||
.bar:after {
|
||||
right:50%;
|
||||
}
|
||||
|
||||
/* active state */
|
||||
input:focus ~ .bar:before, input:focus ~ .bar:after,
|
||||
.group.select input ~ .bar:before, .group.select input ~ .bar:after{
|
||||
width:50%;
|
||||
}
|
||||
|
||||
/* HIGHLIGHTER ================================== */
|
||||
.highlight {
|
||||
position:absolute;
|
||||
height:60%;
|
||||
width:100px;
|
||||
top:25%;
|
||||
left:0;
|
||||
pointer-events:none;
|
||||
opacity:0.5;
|
||||
}
|
||||
|
||||
/* active state */
|
||||
input:focus ~ .highlight {
|
||||
-webkit-animation:inputHighlighter 0.3s ease;
|
||||
-moz-animation:inputHighlighter 0.3s ease;
|
||||
animation:inputHighlighter 0.3s ease;
|
||||
}
|
||||
|
||||
.group .btn{
|
||||
width: 100%;
|
||||
padding: 0px 0px;
|
||||
height: 48px;
|
||||
color: #fff;
|
||||
border: 0 none;
|
||||
font-size: 22px;
|
||||
font-weight: 500;
|
||||
line-height: 34px;
|
||||
color: #202129;
|
||||
background-color: #edeeee;
|
||||
}
|
||||
.group.select .btn{
|
||||
/* border: 6px solid #C0C0C0; */
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.group .btn.btn-green{
|
||||
border: 1px solid #0b81ff;
|
||||
color: #0b81ff;
|
||||
background-color: white;
|
||||
font-weight: 500;
|
||||
border-radius: 28px;
|
||||
}
|
||||
.group.select .btn.btn-green, .group:hover .btn.btn-green{
|
||||
background-image: linear-gradient(to right, #62A3FF, #4994FF , #0b81ff);
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
.group .btn.btn-orange{
|
||||
border: 1px solid #B6B6B6;
|
||||
color: #B6B6B6;
|
||||
background-color: white;
|
||||
font-weight: 500;
|
||||
border-radius: 28px;
|
||||
}
|
||||
.group.select .btn.btn-orange, .group:hover .btn.btn-orange{
|
||||
|
||||
background-image: linear-gradient(to right, #D3D3D3, #C5C5C5 , #B6B6B6);
|
||||
color: white;
|
||||
border: #B6B6B6;
|
||||
}
|
||||
|
||||
.group .btn.btn-red{
|
||||
border: 1px solid #e74c3c80;
|
||||
color: #e74c3c;
|
||||
background-color: white;
|
||||
font-weight: 500;
|
||||
border-radius: 28px;
|
||||
}
|
||||
.group.select .btn.btn-red, .group:hover .btn.btn-red{
|
||||
background-image: linear-gradient(to right, #FF5B5B, #FF4B4B , #FE3C3C);
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* ANIMATIONS ================ */
|
||||
@-webkit-keyframes inputHighlighter {
|
||||
from { background:#5264AE; }
|
||||
to { width:0; background:transparent; }
|
||||
}
|
||||
@-moz-keyframes inputHighlighter {
|
||||
from { background:#5264AE; }
|
||||
to { width:0; background:transparent; }
|
||||
}
|
||||
@keyframes inputHighlighter {
|
||||
from { background:#5264AE; }
|
||||
to { width:0; background:transparent; }
|
||||
}
|
||||
</style>
|
||||
|
||||
80
HTML/gcphone/src/components/contacts/Contacts.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div style="width: 326px; height: 743px; backgroundColor: white" class="contact">
|
||||
<list :list='lcontacts' :disable="disableList" :title="IntlString('APP_CONTACT_TITLE')" @back="back" @select='onSelect' @option='onOption'></list>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import { generateColorForStr } from '@/Utils'
|
||||
import List from './../List.vue'
|
||||
import Modal from '@/components/Modal/index.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
List
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
disableList: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'contacts', 'useMouse']),
|
||||
lcontacts () {
|
||||
let addContact = {display: this.IntlString('APP_CONTACT_NEW'), letter: '+', num: '', id: -1}
|
||||
return [addContact, ...this.contacts.map(e => {
|
||||
e.backgroundColor = e.backgroundColor || generateColorForStr(e.number)
|
||||
return e
|
||||
})]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSelect (contact) {
|
||||
if (contact.id === -1) {
|
||||
this.$router.push({ name: 'contacts.view', params: { id: contact.id } })
|
||||
} else {
|
||||
this.$router.push({ name: 'messages.view', params: { number: contact.number, display: contact.display } })
|
||||
}
|
||||
},
|
||||
onOption (contact) {
|
||||
if (contact.id === -1 || contact.id === undefined) return
|
||||
this.disableList = true
|
||||
Modal.CreateModal({
|
||||
choix: [
|
||||
{id: 1, title: this.IntlString('APP_CONTACT_EDIT'), icons: 'fa-circle-o', color: 'orange'},
|
||||
{id: 3, title: 'Abbrechen', icons: 'fa-undo'}
|
||||
]
|
||||
}).then(rep => {
|
||||
if (rep.id === 1) {
|
||||
this.$router.push({path: 'contact/' + contact.id})
|
||||
}
|
||||
this.disableList = false
|
||||
})
|
||||
},
|
||||
back () {
|
||||
if (this.disableList === true) return
|
||||
this.$router.push({ name: 'home' })
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpBackspace', this.back)
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpBackspace', this.back)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.contact{
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div style="width: 326px; height: 743px; backgroundColor: white" class="contact">
|
||||
<list :list='lcontacts' :title="IntlString('APP_MESSAGE_CONTACT_TITLE')" v-on:select="onSelect" @back="back"></list>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import List from './../List.vue'
|
||||
import { mapGetters } from 'vuex'
|
||||
import Modal from '@/components/Modal/index.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
List
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'contacts', 'useMouse']),
|
||||
lcontacts () {
|
||||
let addContact = {
|
||||
display: this.IntlString('APP_MESSAGE_CONTRACT_ENTER_NUMBER'),
|
||||
letter: '+',
|
||||
backgroundColor: 'orange',
|
||||
num: -1
|
||||
}
|
||||
return [addContact, ...this.contacts]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSelect (contact) {
|
||||
if (contact.num === -1) {
|
||||
Modal.CreateTextModal({
|
||||
title: this.IntlString('APP_PHONE_ENTER_NUMBER'),
|
||||
limit: 10
|
||||
}).then(data => {
|
||||
let message = data.text.trim()
|
||||
if (message !== '') {
|
||||
this.$router.push({
|
||||
name: 'messages.view',
|
||||
params: {
|
||||
number: message,
|
||||
display: message
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.$router.push({name: 'messages.view', params: contact})
|
||||
}
|
||||
},
|
||||
back () {
|
||||
history.back()
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.$bus.$on('keyUpBackspace', this.back)
|
||||
},
|
||||
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpBackspace', this.back)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.contact{
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
526
HTML/gcphone/src/components/messages/Messages.vue
Normal file
@@ -0,0 +1,526 @@
|
||||
<template>
|
||||
<!--ESTE HTML ES ACOPLADO DEL VIEJO-->
|
||||
<div style="width: 330px; height: 743px; backgroundColor: white" class="phone_app messages">
|
||||
<PhoneTitle :title="displayContact" style="backgroundColor: #F1F1F1; color: black" @back="quit"/> <!--:title="displayContact" :backgroundColor="color" -->
|
||||
<div class="img-fullscreen" v-if="imgZoom !== undefined" @click.stop="imgZoom = undefined">
|
||||
<img :src="imgZoom" />
|
||||
</div>
|
||||
|
||||
<textarea ref="copyTextarea" class="copyTextarea"/>
|
||||
|
||||
|
||||
<div style="width: 326px; height: 678px; backgroundColor: white" id='sms_list' @contextmenu.prevent="showOptions">
|
||||
<div class="sms" v-bind:class="{ select: key === selectMessage}" v-for='(mess, key) in messagesList' v-bind:key="mess.id" @click.stop="onActionMessage(mess)"
|
||||
>
|
||||
<div class="sms_message_time">
|
||||
<h6 v-bind:class="{ sms_me : mess.owner === 1}" class="name_other_sms_me">{{displayContact}}</h6>
|
||||
<h6 v-bind:class="{ sms_me : mess.owner === 1}" class="name_other_sms_other" @click.stop="onActionMessage(mess)"><timeago style="font-weight: 500" class="sms_time" :since='mess.time' :auto-update="20"></timeago></h6>
|
||||
</div>
|
||||
<span class='sms_message sms_me'
|
||||
@click.stop="onActionMessage(mess)"
|
||||
|
||||
v-bind:class="{ sms_other : mess.owner === 0}" >
|
||||
|
||||
<img v-if="isSMSImage(mess)" @click.stop="onActionMessage(mess)" class="sms-img" :src="mess.message">
|
||||
<span v-else @click.stop="onActionMessage(mess)" >{{mess.message}}</span>
|
||||
|
||||
<!--<span style="color: white; font-size: 17px; margin: 24px;" @click.stop="onActionMessage(mess)" ><timeago class="sms_time" :since='mess.time' :auto-update="20"></timeago></span>-->
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="width: 306px;" id='sms_write' @contextmenu.prevent="showOptions">
|
||||
<input
|
||||
type="text"
|
||||
v-model="message"
|
||||
:placeholder="IntlString('APP_MESSAGE_PLACEHOLDER_ENTER_MESSAGE')"
|
||||
v-autofocus
|
||||
@keyup.enter.prevent="send"
|
||||
>
|
||||
<div style=" font-size: 10px;" class="sms_send" @click.stop="send">
|
||||
<svg height="24" viewBox="0 0 24 24" width="24" @click.stop="send">
|
||||
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
|
||||
<path d="M0 0h24v24H0z" fill="none"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
import { generateColorForStr, getBestFontColor } from './../../Utils'
|
||||
import PhoneTitle from './../PhoneTitle'
|
||||
import Modal from '@/components/Modal/index.js'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
ignoreControls: false,
|
||||
selectMessage: -1,
|
||||
display: '',
|
||||
phoneNumber: '',
|
||||
imgZoom: undefined,
|
||||
message: ''
|
||||
}
|
||||
},
|
||||
components: {
|
||||
PhoneTitle
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setMessageRead', 'sendMessage', 'deleteMessage', 'startCall']),
|
||||
resetScroll () {
|
||||
this.$nextTick(() => {
|
||||
let elem = document.querySelector('#sms_list')
|
||||
elem.scrollTop = elem.scrollHeight
|
||||
this.selectMessage = -1
|
||||
})
|
||||
},
|
||||
scrollIntoViewIfNeeded () {
|
||||
this.$nextTick(() => {
|
||||
const elem = this.$el.querySelector('.select')
|
||||
if (elem !== null) {
|
||||
elem.scrollIntoViewIfNeeded()
|
||||
}
|
||||
})
|
||||
},
|
||||
quit () {
|
||||
// this.$router.push({path: '/messages'})
|
||||
this.$router.go(-1)
|
||||
},
|
||||
onUp: function () {
|
||||
if (this.ignoreControls === true) return
|
||||
if (this.selectMessage === -1) {
|
||||
this.selectMessage = this.messagesList.length - 1
|
||||
} else {
|
||||
this.selectMessage = this.selectMessage === 0 ? 0 : this.selectMessage - 1
|
||||
}
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
onDown () {
|
||||
if (this.ignoreControls === true) return
|
||||
if (this.selectMessage === -1) {
|
||||
this.selectMessage = this.messagesList.length - 1
|
||||
} else {
|
||||
this.selectMessage = this.selectMessage === this.messagesList.length - 1 ? this.selectMessage : this.selectMessage + 1
|
||||
}
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
onEnter () {
|
||||
if (this.ignoreControls === true) return
|
||||
if (this.selectMessage !== -1) {
|
||||
this.onActionMessage(this.messagesList[this.selectMessage])
|
||||
} else {
|
||||
this.$phoneAPI.getReponseText().then(data => {
|
||||
let message = data.text.trim()
|
||||
if (message !== '') {
|
||||
this.sendMessage({
|
||||
phoneNumber: this.phoneNumber,
|
||||
message
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
send () {
|
||||
const message = this.message.trim()
|
||||
if (message === '') return
|
||||
this.message = ''
|
||||
this.sendMessage({
|
||||
phoneNumber: this.phoneNumber,
|
||||
message
|
||||
})
|
||||
},
|
||||
isSMSImage (mess) {
|
||||
return /^https?:\/\/.*\.(png|jpg|jpeg|gif)/.test(mess.message)
|
||||
},
|
||||
async onActionMessage (message) {
|
||||
try {
|
||||
// let message = this.messagesList[this.selectMessage]
|
||||
let isGPS = /(-?\d+(\.\d+)?), (-?\d+(\.\d+)?)/.test(message.message)
|
||||
let hasNumber = /#([0-9]+)/.test(message.message)
|
||||
let isSMSImage = this.isSMSImage(message)
|
||||
let choix = [{
|
||||
id: 'delete',
|
||||
title: this.IntlString('APP_MESSAGE_DELETE'),
|
||||
icons: 'fa-trash'
|
||||
}, {
|
||||
id: -1,
|
||||
title: this.IntlString('CANCEL'),
|
||||
icons: 'fa-undo'
|
||||
}]
|
||||
if (isGPS === true) {
|
||||
choix = [{
|
||||
id: 'gps',
|
||||
title: this.IntlString('APP_MESSAGE_SET_GPS'),
|
||||
icons: 'fa-location-arrow'
|
||||
}, ...choix]
|
||||
}
|
||||
if (hasNumber === true) {
|
||||
const num = message.message.match(/#([0-9-]*)/)[1]
|
||||
choix = [{
|
||||
id: 'num',
|
||||
title: `${this.IntlString('APP_MESSAGE_MESS_NUMBER')} ${num}`,
|
||||
number: num,
|
||||
icons: 'fa-bars'
|
||||
}, ...choix]
|
||||
}
|
||||
if (isSMSImage === true) {
|
||||
choix = [{
|
||||
id: 'zoom',
|
||||
title: this.IntlString('APP_MESSAGE_ZOOM_IMG'),
|
||||
icons: 'fa-search'
|
||||
}, ...choix]
|
||||
}
|
||||
this.ignoreControls = true
|
||||
const data = await Modal.CreateModal({choix})
|
||||
if (data.id === 'delete') {
|
||||
this.deleteMessage({ id: message.id })
|
||||
} else if (data.id === 'gps') {
|
||||
let val = message.message.match(/(-?\d+(\.\d+)?), (-?\d+(\.\d+)?)/)
|
||||
this.$phoneAPI.setGPS(val[1], val[3])
|
||||
} else if (data.id === 'num') {
|
||||
this.$nextTick(() => {
|
||||
this.onSelectPhoneNumber(data.number)
|
||||
})
|
||||
} else if (data.id === 'zoom') {
|
||||
this.imgZoom = message.message
|
||||
}
|
||||
} catch (e) {
|
||||
} finally {
|
||||
this.ignoreControls = false
|
||||
this.selectMessage = -1
|
||||
}
|
||||
},
|
||||
async onSelectPhoneNumber (number) {
|
||||
try {
|
||||
this.ignoreControls = true
|
||||
let choix = [
|
||||
{
|
||||
id: 'sms',
|
||||
title: this.IntlString('APP_MESSAGE_MESS_SMS'),
|
||||
icons: 'fa-comment'
|
||||
},
|
||||
{
|
||||
id: 'call',
|
||||
title: this.IntlString('APP_MESSAGE_MESS_CALL'),
|
||||
icons: 'fa-phone'
|
||||
}
|
||||
]
|
||||
// if (this.useMouse === true) {
|
||||
choix.push({
|
||||
id: 'copy',
|
||||
title: this.IntlString('APP_MESSAGE_MESS_COPY'),
|
||||
icons: 'fa-copy'
|
||||
})
|
||||
// }
|
||||
choix.push({
|
||||
id: -1,
|
||||
title: this.IntlString('CANCEL'),
|
||||
icons: 'fa-undo'
|
||||
})
|
||||
const data = await Modal.CreateModal({ choix })
|
||||
if (data.id === 'sms') {
|
||||
this.phoneNumber = number
|
||||
this.display = undefined
|
||||
} else if (data.id === 'call') {
|
||||
this.startCall({ numero: number })
|
||||
} else if (data.id === 'copy') {
|
||||
try {
|
||||
const $copyTextarea = this.$refs.copyTextarea
|
||||
$copyTextarea.value = number
|
||||
$copyTextarea.style.height = '20px'
|
||||
$copyTextarea.focus()
|
||||
$copyTextarea.select()
|
||||
await document.execCommand('copy')
|
||||
$copyTextarea.style.height = '0'
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} finally {
|
||||
this.ignoreControls = false
|
||||
this.selectMessage = -1
|
||||
}
|
||||
},
|
||||
onBackspace () {
|
||||
if (this.imgZoom !== undefined) {
|
||||
this.imgZoom = undefined
|
||||
return
|
||||
}
|
||||
if (this.ignoreControls === true) return
|
||||
if (this.useMouse === true && document.activeElement.tagName !== 'BODY') return
|
||||
if (this.selectMessage !== -1) {
|
||||
this.selectMessage = -1
|
||||
} else {
|
||||
this.quit()
|
||||
}
|
||||
},
|
||||
async showOptions () {
|
||||
try {
|
||||
this.ignoreControls = true
|
||||
let choix = [
|
||||
{id: 1, title: this.IntlString('APP_MESSAGE_SEND_GPS'), icons: 'fa-location-arrow'},
|
||||
{id: -1, title: this.IntlString('CANCEL'), icons: 'fa-undo'}
|
||||
]
|
||||
if (this.enableTakePhoto) {
|
||||
choix = [
|
||||
{id: 1, title: this.IntlString('APP_MESSAGE_SEND_GPS'), icons: 'fa-location-arrow'},
|
||||
{id: 2, title: this.IntlString('APP_MESSAGE_SEND_PHOTO'), icons: 'fa-picture-o'},
|
||||
{id: -1, title: this.IntlString('CANCEL'), icons: 'fa-undo'}
|
||||
]
|
||||
}
|
||||
const data = await Modal.CreateModal({ choix })
|
||||
if (data.id === 1) {
|
||||
this.sendMessage({
|
||||
phoneNumber: this.phoneNumber,
|
||||
message: '%pos%'
|
||||
})
|
||||
}
|
||||
if (data.id === 2) {
|
||||
const { url } = await this.$phoneAPI.takePhoto()
|
||||
if (url !== null && url !== undefined) {
|
||||
this.sendMessage({
|
||||
phoneNumber: this.phoneNumber,
|
||||
message: url
|
||||
})
|
||||
}
|
||||
}
|
||||
this.ignoreControls = false
|
||||
} catch (e) {
|
||||
} finally {
|
||||
this.ignoreControls = false
|
||||
}
|
||||
},
|
||||
onRight: function () {
|
||||
if (this.ignoreControls === true) return
|
||||
if (this.selectMessage === -1) {
|
||||
this.showOptions()
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'messages', 'contacts', 'useMouse', 'enableTakePhoto']),
|
||||
messagesList () {
|
||||
return this.messages.filter(e => e.transmitter === this.phoneNumber).sort((a, b) => a.time - b.time)
|
||||
},
|
||||
displayContact () {
|
||||
if (this.display !== undefined) {
|
||||
return this.display
|
||||
}
|
||||
const c = this.contacts.find(c => c.number === this.phoneNumber)
|
||||
if (c !== undefined) {
|
||||
return c.display
|
||||
}
|
||||
return this.phoneNumber
|
||||
},
|
||||
color () {
|
||||
return generateColorForStr(this.phoneNumber)
|
||||
},
|
||||
colorSmsOwner () {
|
||||
return [
|
||||
{
|
||||
backgroundColor: this.color,
|
||||
color: getBestFontColor(this.color)
|
||||
}, {}
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
messagesList () {
|
||||
this.setMessageRead(this.phoneNumber)
|
||||
this.resetScroll()
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.display = this.$route.params.display
|
||||
this.phoneNumber = this.$route.params.number
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$on('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
this.$bus.$on('keyUpArrowRight', this.onRight)
|
||||
}
|
||||
this.$bus.$on('keyUpBackspace', this.onBackspace)
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$off('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
this.$bus.$off('keyUpArrowRight', this.onRight)
|
||||
this.$bus.$off('keyUpBackspace', this.onBackspace)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.messages{
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 326px;
|
||||
height: 678px;
|
||||
right: 0;
|
||||
height: calc(100% - 20px);
|
||||
background-color: #DDD;
|
||||
}
|
||||
#sms_contact{
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
height: 34px;
|
||||
line-height: 34px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
#sms_list{
|
||||
height: calc(100% - 34px - 26px);
|
||||
overflow-y: auto;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.name_other_sms_other{
|
||||
margin-bottom: -9px;
|
||||
margin-left: 42px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: lightgrey;
|
||||
}
|
||||
|
||||
.name_other_sms_me{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.name_other_sms_other.sms_me{
|
||||
display: none;
|
||||
|
||||
}
|
||||
|
||||
.sms{
|
||||
overflow: auto;
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
.sms-img{
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 19px;
|
||||
}
|
||||
.img-fullscreen {
|
||||
position: fixed;
|
||||
z-index: 999999;
|
||||
background-color: rgba(20, 20, 20, 0.8);
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.img-fullscreen img {
|
||||
display: flex;
|
||||
max-width: 90vw;
|
||||
max-height: 95vh;
|
||||
}
|
||||
|
||||
.sms_me{
|
||||
float: right;
|
||||
background-color: #e9e9eb;
|
||||
border-radius: 17px;
|
||||
padding: 5px 10px;
|
||||
max-width: 90%;
|
||||
margin-right: 5%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.sms_other{
|
||||
background-color: #0b81ff;
|
||||
border-radius: 17px;
|
||||
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
||||
color:white;
|
||||
float: left;
|
||||
padding: 5px 10px;
|
||||
max-width: 90%;
|
||||
margin-left: 5%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
|
||||
.sms_time{
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.sms_me .sms_time{
|
||||
color: #AAA;
|
||||
margin-left: 4px;
|
||||
margin-top: -5px;
|
||||
display: none;
|
||||
font-size: 9px;
|
||||
|
||||
}
|
||||
.sms_other .sms_time{
|
||||
color: white;
|
||||
display: none;
|
||||
margin-left: 4px;
|
||||
margin-top: -5px;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
|
||||
.messages{
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.sms.select .sms_message, .sms_message:hover{
|
||||
background-color: #373B3C !important;
|
||||
|
||||
color: #E4E3E2 !important;
|
||||
}
|
||||
|
||||
.sms.select .sms_message, .sms_message:hover{
|
||||
background-color: #373B3C !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.sms_message{
|
||||
word-wrap: break-word;
|
||||
max-width: 80%;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
#sms_write{
|
||||
height: 56px;
|
||||
margin: 10px;
|
||||
width: 380px;
|
||||
background-color: #e9e9eb;
|
||||
border-radius: 56px;
|
||||
}
|
||||
#sms_write input{
|
||||
height: 56px;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: 16px;
|
||||
margin-left: 14px;
|
||||
padding: 12px 5px;
|
||||
background-color: rgba(236, 236, 241, 0)
|
||||
}
|
||||
|
||||
.sms_send{
|
||||
float: right;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.sms_send svg{
|
||||
margin: 8px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
fill: #C0C0C0;
|
||||
}
|
||||
.copyTextarea {
|
||||
height: 0;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
144
HTML/gcphone/src/components/messages/MessagesList.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<div style="width: 326px; height: 743px;" class="screen">
|
||||
<list style="color: black" :list='messagesData' :disable="disableList" :title="IntlString('APP_MESSAGE_TITLE')" @back="back" @select="onSelect" @option='onOption'></list>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
import { generateColorForStr } from '@/Utils'
|
||||
import Modal from '@/components/Modal/index.js'
|
||||
import List from '@/components/List'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
List
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
disableList: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['deleteMessagesNumber', 'deleteAllMessages', 'startCall']),
|
||||
onSelect: function (data) {
|
||||
if (data.id === -1) {
|
||||
this.$router.push({name: 'messages.selectcontact'})
|
||||
} else {
|
||||
this.$router.push({name: 'messages.view', params: data})
|
||||
}
|
||||
},
|
||||
onOption: function (data) {
|
||||
if (data.number === undefined) return
|
||||
this.disableList = true
|
||||
Modal.CreateModal({
|
||||
choix: [
|
||||
{id: 4, title: this.IntlString('APP_PHONE_CALL'), icons: 'fa-phone'},
|
||||
{id: 5, title: this.IntlString('APP_PHONE_CALL_ANONYMOUS'), icons: 'fa-mask'},
|
||||
{id: 6, title: this.IntlString('APP_MESSAGE_NEW_MESSAGE'), icons: 'fa-sms'},
|
||||
{id: 1, title: this.IntlString('APP_MESSAGE_ERASE_CONVERSATION'), icons: 'fa-trash', color: 'orange'},
|
||||
/* {id: 2, title: this.IntlString('APP_MESSAGE_ERASE_ALL_CONVERSATIONS'), icons: 'fa-trash', color: 'red'},
|
||||
{id: 3, title: this.IntlString('CANCEL'), icons: 'fa-undo'} */
|
||||
{id: 2, title: this.IntlString('APP_MESSAGE_ERASE_ALL_CONVERSATIONS'), icons: 'fa-trash', color: 'red'}
|
||||
]
|
||||
.concat(data.unknowContact ? [{id: 7, title: this.IntlString('APP_MESSAGE_SAVE_CONTACT'), icons: 'fa-save'}] : [])
|
||||
.concat([{id: 3, title: this.IntlString('CANCEL'), icons: 'fa-undo'}])
|
||||
}).then(rep => {
|
||||
if (rep.id === 1) {
|
||||
this.deleteMessagesNumber({num: data.number})
|
||||
} else if (rep.id === 2) {
|
||||
this.deleteAllMessages()
|
||||
} else if (rep.id === 4) {
|
||||
this.startCall({ numero: data.number })
|
||||
} else if (rep.id === 5) {
|
||||
this.startCall({ numero: '#' + data.number })
|
||||
} else if (rep.id === 6) {
|
||||
this.$router.push({name: 'messages.view', params: data})
|
||||
} else if (rep.id === 7) {
|
||||
this.$router.push({name: 'contacts.view', params: {id: 0, number: data.number}})
|
||||
}
|
||||
this.disableList = false
|
||||
})
|
||||
},
|
||||
back: function () {
|
||||
if (this.disableList === true) return
|
||||
this.$router.push({ name: 'home' })
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'useMouse', 'contacts', 'messages']),
|
||||
messagesData: function () {
|
||||
let messages = this.messages
|
||||
let contacts = this.contacts
|
||||
let messGroup = messages.reduce((rv, x) => {
|
||||
if (rv[x['transmitter']] === undefined) {
|
||||
const data = {
|
||||
noRead: 0,
|
||||
lastMessage: 0,
|
||||
display: x.transmitter
|
||||
}
|
||||
let contact = contacts.find(e => e.number === x.transmitter)
|
||||
data.unknowContact = contact === undefined
|
||||
if (contact !== undefined) {
|
||||
data.display = contact.display
|
||||
data.backgroundColor = contact.backgroundColor || generateColorForStr(x.transmitter)
|
||||
data.letter = contact.letter
|
||||
data.icon = contact.icon
|
||||
} else {
|
||||
data.backgroundColor = generateColorForStr(x.transmitter)
|
||||
}
|
||||
rv[x['transmitter']] = data
|
||||
}
|
||||
if (x.isRead === 0) {
|
||||
rv[x['transmitter']].noRead += 1
|
||||
}
|
||||
if (x.time >= rv[x['transmitter']].lastMessage) {
|
||||
rv[x['transmitter']].lastMessage = x.time
|
||||
rv[x['transmitter']].keyDesc = x.message
|
||||
}
|
||||
return rv
|
||||
}, {})
|
||||
let mess = []
|
||||
Object.keys(messGroup).forEach(key => {
|
||||
mess.push({
|
||||
display: messGroup[key].display,
|
||||
puce: messGroup[key].noRead,
|
||||
number: key,
|
||||
lastMessage: messGroup[key].lastMessage,
|
||||
keyDesc: messGroup[key].keyDesc,
|
||||
backgroundColor: messGroup[key].backgroundColor,
|
||||
icon: messGroup[key].icon,
|
||||
letter: messGroup[key].letter,
|
||||
unknowContact: messGroup[key].unknowContact
|
||||
})
|
||||
})
|
||||
mess.sort((a, b) => b.lastMessage - a.lastMessage)
|
||||
return [this.newMessageOption, ...mess]
|
||||
},
|
||||
newMessageOption () {
|
||||
return {
|
||||
backgroundColor: '#C0C0C0',
|
||||
display: this.IntlString('APP_MESSAGE_NEW_MESSAGE'),
|
||||
letter: '+',
|
||||
id: -1
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.$bus.$on('keyUpBackspace', this.back)
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpBackspace', this.back)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.screen{
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
345
HTML/gcphone/src/components/parametre/Parametre.vue
Normal file
@@ -0,0 +1,345 @@
|
||||
<template>
|
||||
<div class="phone_app">
|
||||
<PhoneTitle :title="IntlString('APP_CONFIG_TITLE')" @back="onBackspace"/>
|
||||
<div class='phone_content elements'>
|
||||
<div class='element'
|
||||
v-for='(elem, key) in paramList'
|
||||
v-bind:class="{ select: key === currentSelect}"
|
||||
v-bind:key="key"
|
||||
@click.stop="onPressItem(key)"
|
||||
>
|
||||
<i class="fa" v-bind:class="elem.icons" v-bind:style="{color: elem.color}" @click.stop="onPressItem(key)"></i>
|
||||
<div class="element-content" @click.stop="onPressItem(key)">
|
||||
<span class="element-title" @click.stop="onPressItem(key)">{{elem.title}}</span>
|
||||
<span v-if="elem.value" class="element-value" @click.stop="onPressItem(key)">{{elem.value}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
import PhoneTitle from './../PhoneTitle'
|
||||
import Modal from '@/components/Modal/index.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PhoneTitle
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
ignoreControls: false,
|
||||
currentSelect: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'useMouse', 'myPhoneNumber', 'backgroundLabel', 'coqueLabel', 'sonidoLabel', 'zoom', 'config', 'volume', 'availableLanguages']),
|
||||
paramList () {
|
||||
const cancelStr = this.IntlString('CANCEL')
|
||||
const confirmResetStr = this.IntlString('APP_CONFIG_RESET_CONFIRM')
|
||||
const cancelOption = {}
|
||||
const confirmReset = {}
|
||||
cancelOption[cancelStr] = 'cancel'
|
||||
confirmReset[confirmResetStr] = 'accept'
|
||||
return [
|
||||
{
|
||||
icons: 'fa-phone',
|
||||
title: this.IntlString('APP_CONFIG_MY_MUNBER'),
|
||||
value: this.myPhoneNumber
|
||||
},
|
||||
{
|
||||
icons: 'fa-picture-o',
|
||||
title: this.IntlString('APP_CONFIG_WALLPAPER'),
|
||||
value: this.backgroundLabel,
|
||||
onValid: 'onChangeBackground',
|
||||
values: this.config.background
|
||||
},
|
||||
{
|
||||
icons: 'fa-mobile',
|
||||
title: this.IntlString('APP_CONFIG_CASE'),
|
||||
value: this.coqueLabel,
|
||||
onValid: 'onChangeCoque',
|
||||
values: this.config.coque
|
||||
},
|
||||
{
|
||||
icons: 'fa-bell-o',
|
||||
title: this.IntlString('APP_CONFIG_SOUND'),
|
||||
value: this.sonidoLabel,
|
||||
onValid: 'onChangeSonido',
|
||||
values: this.config.sonido
|
||||
},
|
||||
{
|
||||
icons: 'fa-search',
|
||||
title: this.IntlString('APP_CONFIG_ZOOM'),
|
||||
value: this.zoom,
|
||||
onValid: 'setZoom',
|
||||
onLeft: this.ajustZoom(-1),
|
||||
onRight: this.ajustZoom(1),
|
||||
values: {
|
||||
'125 %': '125%',
|
||||
'100 %': '100%',
|
||||
'80 %': '80%',
|
||||
'60 %': '60%',
|
||||
'40 %': '40%',
|
||||
'20 %': '20%'
|
||||
}
|
||||
},
|
||||
{
|
||||
icons: 'fa-volume-down',
|
||||
title: this.IntlString('APP_CONFIG_VOLUME'),
|
||||
value: this.valumeDisplay,
|
||||
onValid: 'setPhoneVolume',
|
||||
onLeft: this.ajustVolume(-0.01),
|
||||
onRight: this.ajustVolume(0.01),
|
||||
values: {
|
||||
'100 %': 1,
|
||||
'80 %': 0.8,
|
||||
'60 %': 0.6,
|
||||
'40 %': 0.4,
|
||||
'20 %': 0.2,
|
||||
'0 %': 0
|
||||
}
|
||||
},
|
||||
/* {
|
||||
icons: 'fa-globe',
|
||||
title: this.IntlString('APP_CONFIG_LANGUAGE'),
|
||||
onValid: 'onChangeLanguages',
|
||||
values: {
|
||||
...this.availableLanguages,
|
||||
...cancelOption
|
||||
}
|
||||
}, */
|
||||
{
|
||||
icons: 'fa-mouse-pointer',
|
||||
title: this.IntlString('APP_CONFIG_MOUSE_SUPPORT'),
|
||||
onValid: 'onChangeMouseSupport',
|
||||
values: {
|
||||
'Yes': true,
|
||||
'No': false,
|
||||
...cancelOption
|
||||
}
|
||||
},
|
||||
{
|
||||
icons: 'fa-exclamation-triangle',
|
||||
color: '#ee3838',
|
||||
title: this.IntlString('APP_CONFIG_RESET'),
|
||||
onValid: 'resetPhone',
|
||||
values: {
|
||||
...confirmReset,
|
||||
...cancelOption
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
valumeDisplay () {
|
||||
return `${Math.floor(this.volume * 100)} %`
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['getIntlString', 'setZoon', 'setBackground', 'setCoque', 'setSonido', 'setVolume', 'setLanguage', 'setMouseSupport']),
|
||||
scrollIntoViewIfNeeded: function () {
|
||||
this.$nextTick(() => {
|
||||
document.querySelector('.select').scrollIntoViewIfNeeded()
|
||||
})
|
||||
},
|
||||
onBackspace () {
|
||||
if (this.ignoreControls === true) return
|
||||
this.$router.push({ name: 'home' })
|
||||
},
|
||||
onUp: function () {
|
||||
if (this.ignoreControls === true) return
|
||||
this.currentSelect = this.currentSelect === 0 ? 0 : this.currentSelect - 1
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
onDown: function () {
|
||||
if (this.ignoreControls === true) return
|
||||
this.currentSelect = this.currentSelect === this.paramList.length - 1 ? this.currentSelect : this.currentSelect + 1
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
onRight () {
|
||||
if (this.ignoreControls === true) return
|
||||
let param = this.paramList[this.currentSelect]
|
||||
if (param.onRight !== undefined) {
|
||||
param.onRight(param)
|
||||
}
|
||||
},
|
||||
onLeft () {
|
||||
if (this.ignoreControls === true) return
|
||||
let param = this.paramList[this.currentSelect]
|
||||
if (param.onLeft !== undefined) {
|
||||
param.onLeft(param)
|
||||
}
|
||||
},
|
||||
actionItem (param) {
|
||||
if (param.values !== undefined) {
|
||||
this.ignoreControls = true
|
||||
let choix = Object.keys(param.values).map(key => {
|
||||
return {title: key, value: param.values[key], picto: param.values[key]}
|
||||
})
|
||||
Modal.CreateModal({choix}).then(reponse => {
|
||||
this.ignoreControls = false
|
||||
if (reponse.title === 'cancel') return
|
||||
this[param.onValid](param, reponse)
|
||||
})
|
||||
}
|
||||
},
|
||||
onPressItem (index) {
|
||||
this.actionItem(this.paramList[index])
|
||||
},
|
||||
onEnter () {
|
||||
if (this.ignoreControls === true) return
|
||||
this.actionItem(this.paramList[this.currentSelect])
|
||||
},
|
||||
async onChangeBackground (param, data) {
|
||||
let val = data.value
|
||||
if (val === 'URL') {
|
||||
this.ignoreControls = true
|
||||
Modal.CreateTextModal({
|
||||
text: 'https://i.imgur.com/'
|
||||
}).then(valueText => {
|
||||
if (valueText.text !== '' && valueText.text !== undefined && valueText.text !== null && valueText.text !== 'https://i.imgur.com/') {
|
||||
this.setBackground({
|
||||
label: 'Custom',
|
||||
value: valueText.text
|
||||
})
|
||||
}
|
||||
}).finally(() => {
|
||||
this.ignoreControls = false
|
||||
})
|
||||
} else {
|
||||
this.setBackground({
|
||||
label: data.title,
|
||||
value: data.value
|
||||
})
|
||||
}
|
||||
},
|
||||
onChangeCoque: function (param, data) {
|
||||
this.setCoque({
|
||||
label: data.title,
|
||||
value: data.value
|
||||
})
|
||||
},
|
||||
|
||||
onChangeSonido: function (param, data) {
|
||||
this.setSonido({
|
||||
label: data.title,
|
||||
value: data.value
|
||||
})
|
||||
},
|
||||
|
||||
setZoom: function (param, data) {
|
||||
this.setZoon(data.value)
|
||||
},
|
||||
ajustZoom (inc) {
|
||||
return () => {
|
||||
const percent = Math.max(10, (parseInt(this.zoom) || 100) + inc)
|
||||
this.setZoon(`${percent}%`)
|
||||
}
|
||||
},
|
||||
setPhoneVolume (param, data) {
|
||||
this.setVolume(data.value)
|
||||
},
|
||||
ajustVolume (inc) {
|
||||
return () => {
|
||||
const newVolume = Math.max(0, Math.min(1, parseFloat(this.volume) + inc))
|
||||
this.setVolume(newVolume)
|
||||
}
|
||||
},
|
||||
onChangeLanguages (param, data) {
|
||||
if (data.value !== 'cancel') {
|
||||
this.setLanguage(data.value)
|
||||
}
|
||||
},
|
||||
onChangeMouseSupport (param, data) {
|
||||
if (data.value !== 'cancel') {
|
||||
this.setMouseSupport(data.value)
|
||||
this.onBackspace()
|
||||
}
|
||||
},
|
||||
resetPhone: function (param, data) {
|
||||
if (data.value !== 'cancel') {
|
||||
this.ignoreControls = true
|
||||
const cancelStr = this.IntlString('CANCEL')
|
||||
const confirmResetStr = this.IntlString('APP_CONFIG_RESET_CONFIRM')
|
||||
let choix = [{title: cancelStr}, {title: cancelStr}, {title: confirmResetStr, color: 'red', reset: true}, {title: cancelStr}, {title: cancelStr}]
|
||||
Modal.CreateModal({choix}).then(reponse => {
|
||||
this.ignoreControls = false
|
||||
if (reponse.reset === true) {
|
||||
this.$phoneAPI.deleteALL()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpArrowRight', this.onRight)
|
||||
this.$bus.$on('keyUpArrowLeft', this.onLeft)
|
||||
this.$bus.$on('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$on('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
} else {
|
||||
this.currentSelect = -1
|
||||
}
|
||||
this.$bus.$on('keyUpBackspace', this.onBackspace)
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpArrowRight', this.onRight)
|
||||
this.$bus.$off('keyUpArrowLeft', this.onLeft)
|
||||
this.$bus.$off('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$off('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
this.$bus.$off('keyUpBackspace', this.onBackspace)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.element{
|
||||
height: 58px;
|
||||
line-height: 58px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
.element .fa{
|
||||
color: #0b81ff;
|
||||
margin-left: 6px;
|
||||
height: 52px;
|
||||
width: 52px;
|
||||
text-align: center;
|
||||
line-height: 52px;
|
||||
}
|
||||
.element-content{
|
||||
display: block;
|
||||
height: 58px;
|
||||
width: 100%;
|
||||
margin-left: 6px;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.element-title{
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
||||
}
|
||||
.element-value{
|
||||
display: block;
|
||||
line-height: 16px;
|
||||
height: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 100;
|
||||
color: #808080;
|
||||
}
|
||||
.element.select, .element:hover{
|
||||
background-color: #DDD;
|
||||
}
|
||||
</style>
|
||||
587
HTML/gcphone/src/components/twitter/TwitterAccount.vue
Normal file
@@ -0,0 +1,587 @@
|
||||
<template>
|
||||
<div style="width: 314px; height: 577px;" class='phone_content content inputText'>
|
||||
<template v-if="state === STATES.MENU">
|
||||
<template v-if="!isLogin">
|
||||
<div class="group" data-type="button" @click.stop="state = STATES.LOGIN">
|
||||
<input type='button' class="btn btn-blue" @click.stop="state = STATES.LOGIN" :value="IntlString('APP_TWITTER_ACCOUNT_LOGIN')"/>
|
||||
</div>
|
||||
|
||||
<div class="group" data-type="button" @click.stop="state = STATES.NOTIFICATION">
|
||||
<input type='button' class="btn btn-blue" @click.stop="state = STATES.NOTIFICATION" :value="IntlString('APP_TWITTER_NOTIFICATION')" />
|
||||
</div>
|
||||
|
||||
<div class="group bottom" data-type="button" @click.stop="state = STATES.NEW_ACCOUNT">
|
||||
<input type='button' class="btn btn-red" @click.stop="state = STATES.NEW_ACCOUNT" :value="IntlString('APP_TWITTER_ACCOUNT_NEW')" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-if="isLogin">
|
||||
<img :src="twitterAvatarUrl" height="128" width="128" style="align-self: center;">
|
||||
|
||||
<div class="group" data-type="button" @click.stop="state = STATES.ACCOUNT">
|
||||
<input type='button' class="btn btn-blue" @click.stop="state = STATES.ACCOUNT" :value="IntlString('APP_TWITTER_ACCOUNT_PARAM')" />
|
||||
</div>
|
||||
|
||||
<div class="group" data-type="button" @click.stop="state = STATES.NOTIFICATION">
|
||||
<input type='button' class="btn btn-blue" @click.stop="state = STATES.NOTIFICATION" :value="IntlString('APP_TWITTER_NOTIFICATION')" />
|
||||
</div>
|
||||
|
||||
<div class="group bottom" data-type="button" @click.stop="logout">
|
||||
<input type='button' class="btn btn-red" @click.stop="logout" :value="IntlString('APP_TWITTER_ACCOUNT_LOGOUT')" />
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-else-if="state === STATES.LOGIN">
|
||||
<div class="group inputText" data-type="text" data-maxlength='64' :data-defaultValue="localAccount.username">
|
||||
<input type="text" :value="localAccount.username" @change="setLocalAccount($event, 'username')">
|
||||
<span class="highlight"></span>
|
||||
<span class="bar"></span>
|
||||
<label>{{ IntlString('APP_TWITTER_ACCOUNT_USERNAME') }}</label>
|
||||
</div>
|
||||
|
||||
<div class="group inputText" data-type="text" data-model='password' data-maxlength='30'>
|
||||
<input autocomplete="new-password" type="password" :value="localAccount.password" @change="setLocalAccount($event, 'password')">
|
||||
<span class="highlight"></span>
|
||||
<span class="bar"></span>
|
||||
<label>{{ IntlString('APP_TWITTER_ACCOUNT_PASSWORD') }}</label>
|
||||
</div>
|
||||
|
||||
<div class="group" data-type="button" @click.stop="login">
|
||||
<input type='button' class="btn btn-blue" @click.stop="login" :value="IntlString('APP_TWITTER_ACCOUNT_LOGIN')" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else-if="state === STATES.NOTIFICATION">
|
||||
<div class="groupCheckBoxTitle">
|
||||
<label>{{ IntlString('APP_TWITTER_NOTIFICATION_WHEN') }}</label>
|
||||
</div>
|
||||
|
||||
<label class="group checkbox" data-type="button" @click.prevent.stop="setNotification(2)">
|
||||
<input type="checkbox" :checked="twitterNotification === 2" @click.prevent.stop="setNotification(2)">
|
||||
{{ IntlString('APP_TWITTER_NOTIFICATION_ALL') }}
|
||||
</label>
|
||||
|
||||
<label class="group checkbox" data-type="button" @click.prevent.stop="setNotification(1)">
|
||||
<input type="checkbox" :checked="twitterNotification === 1" @click.prevent.stop="setNotification(1)">
|
||||
{{ IntlString('APP_TWITTER_NOTIFICATION_MENTION') }}
|
||||
</label>
|
||||
|
||||
<label class="group checkbox" data-type="button" @click.prevent.stop="setNotification(0)">
|
||||
<input type="checkbox" :checked="twitterNotification === 0" @click.prevent.stop="setNotification(0)">
|
||||
{{ IntlString('APP_TWITTER_NOTIFICATION_NEVER') }}
|
||||
</label>
|
||||
|
||||
<div class="groupCheckBoxTitle">
|
||||
<label>{{ IntlString('APP_TWITTER_NOTIFICATION_SOUND') }}</label>
|
||||
</div>
|
||||
|
||||
<label class="group checkbox" data-type="button" @click.prevent.stop="setNotificationSound(true)">
|
||||
<input type="checkbox" :checked="twitterNotificationSound" @click.prevent.stop="setNotificationSound(true)">
|
||||
{{ IntlString('APP_TWITTER_NOTIFICATION_SOUND_YES') }}
|
||||
</label>
|
||||
|
||||
<label class="group checkbox" data-type="button" @click.prevent.stop="setNotificationSound(false)">
|
||||
<input type="checkbox" :checked="!twitterNotificationSound" @click.prevent.stop="setNotificationSound(false)">
|
||||
{{ IntlString('APP_TWITTER_NOTIFICATION_SOUND_NO') }}
|
||||
</label>
|
||||
|
||||
</template>
|
||||
|
||||
<template v-else-if="state === STATES.ACCOUNT">
|
||||
|
||||
<div style="margin-top: 42px; margin-bottom: 42px;" class="group img" data-type="button" @click.stop="onPressChangeAvartar">
|
||||
<img :src="twitterAvatarUrl" height="128" width="128" @click.stop="onPressChangeAvartar">
|
||||
<input type='button' class="btn btn-blue" :value="IntlString('APP_TWITTER_ACCOUNT_AVATAR')" @click.stop="onPressChangeAvartar" />
|
||||
</div>
|
||||
|
||||
<div class="group" data-type="button" @click.stop="changePassword">
|
||||
<input type='button' class="btn btn-red" :value="IntlString('APP_TWITTER_ACCOUNT_CHANGE_PASSWORD')" @click.stop="changePassword"/>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<template v-else-if="state === STATES.NEW_ACCOUNT">
|
||||
|
||||
<div class="group inputText" data-type="text" data-maxlength='64' data-defaultValue="">
|
||||
<input type="text" :value="localAccount.username" @change="setLocalAccount($event, 'username')">
|
||||
<span class="highlight"></span>
|
||||
<span class="bar"></span>
|
||||
<label>{{ IntlString('APP_TWITTER_NEW_ACCOUNT_USERNAME') }}</label>
|
||||
</div>
|
||||
|
||||
<div class="group inputText" data-type="text" data-model='password' data-maxlength='30'>
|
||||
<input autocomplete="new-password" type="password" :value="localAccount.password" @change="setLocalAccount($event, 'password')">
|
||||
<span class="highlight"></span>
|
||||
<span class="bar"></span>
|
||||
<label>{{ IntlString('APP_TWITTER_NEW_ACCOUNT_PASSWORD') }}</label>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="group inputText" data-type="text" data-model='password' data-maxlength='30'>
|
||||
<input autocomplete="new-password" type="password" :value="localAccount.passwordConfirm" @change="setLocalAccount($event, 'passwordConfirm')">
|
||||
<span class="highlight"></span>
|
||||
<span class="bar"></span>
|
||||
<label>{{ IntlString('APP_TWITTER_NEW_ACCOUNT_PASSWORD_CONFIRM') }}</label>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 42px; margin-bottom: 42px;" class="group img" data-type="button" @click.stop="setLocalAccountAvartar($event)">
|
||||
<img :src="localAccount.avatarUrl" height="128" width="128" @click.stop="setLocalAccountAvartar($event)">
|
||||
<input type='button' class="btn btn-blue" :value="IntlString('APP_TWITTER_NEW_ACCOUNT_AVATAR')" @click.stop="setLocalAccountAvartar($event)"/>
|
||||
</div>
|
||||
|
||||
<div class="group" data-type="button" @click.stop="createAccount">
|
||||
<input type='button' class="btn" :class="validAccount ? 'btn-blue' : 'btn-gray'" :value="IntlString('APP_TWIITER_ACCOUNT_CREATE')" @click.stop="createAccount"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
import Modal from '@/components/Modal'
|
||||
|
||||
const STATES = Object.freeze({
|
||||
MENU: 0,
|
||||
NEW_ACCOUNT: 1,
|
||||
LOGIN: 2,
|
||||
ACCOUNT: 3,
|
||||
NOTIFICATION: 4
|
||||
})
|
||||
export default {
|
||||
components: {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
STATES,
|
||||
state: STATES.MENU,
|
||||
localAccount: {
|
||||
username: '',
|
||||
password: '',
|
||||
passwordConfirm: '',
|
||||
avatarUrl: null
|
||||
},
|
||||
notification: 0,
|
||||
notificationSound: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'useMouse', 'twitterUsername', 'twitterPassword', 'twitterAvatarUrl', 'twitterNotification', 'twitterNotificationSound']),
|
||||
isLogin () {
|
||||
return this.twitterUsername !== undefined && this.twitterUsername !== ''
|
||||
},
|
||||
validAccount () {
|
||||
return this.localAccount.username.length >= 4 && this.localAccount.password.length >= 6 && this.localAccount.password === this.localAccount.passwordConfirm
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['twitterLogin', 'twitterChangePassword', 'twitterLogout', 'twitterSetAvatar', 'twitterCreateNewAccount', 'setTwitterNotification', 'setTwitterNotificationSound']),
|
||||
onUp: function () {
|
||||
if (this.ignoreControls === true) return
|
||||
let select = document.querySelector('.group.select')
|
||||
if (select === null) {
|
||||
select = document.querySelector('.group')
|
||||
select.classList.add('select')
|
||||
return
|
||||
}
|
||||
while (select.previousElementSibling !== null) {
|
||||
if (select.previousElementSibling.classList.contains('group')) {
|
||||
break
|
||||
}
|
||||
select = select.previousElementSibling
|
||||
}
|
||||
if (select.previousElementSibling !== null) {
|
||||
document.querySelectorAll('.group').forEach(elem => {
|
||||
elem.classList.remove('select')
|
||||
})
|
||||
select.previousElementSibling.classList.add('select')
|
||||
let i = select.previousElementSibling.querySelector('input')
|
||||
if (i !== null) {
|
||||
i.focus()
|
||||
}
|
||||
}
|
||||
},
|
||||
onDown: function () {
|
||||
if (this.ignoreControls === true) return
|
||||
let select = document.querySelector('.group.select')
|
||||
if (select === null) {
|
||||
select = document.querySelector('.group')
|
||||
select.classList.add('select')
|
||||
return
|
||||
}
|
||||
while (select.nextElementSibling !== null) {
|
||||
if (select.nextElementSibling.classList.contains('group')) {
|
||||
break
|
||||
}
|
||||
select = select.nextElementSibling
|
||||
}
|
||||
if (select.nextElementSibling !== null) {
|
||||
document.querySelectorAll('.group').forEach(elem => {
|
||||
elem.classList.remove('select')
|
||||
})
|
||||
select.nextElementSibling.classList.add('select')
|
||||
let i = select.nextElementSibling.querySelector('input')
|
||||
if (i !== null) {
|
||||
i.focus()
|
||||
}
|
||||
}
|
||||
},
|
||||
onEnter: function () {
|
||||
if (this.ignoreControls === true) return
|
||||
let select = document.querySelector('.group.select')
|
||||
if (select === null) return
|
||||
|
||||
if (select.dataset !== null) {
|
||||
if (select.dataset.type === 'text') {
|
||||
const $input = select.querySelector('input')
|
||||
let options = {
|
||||
limit: parseInt(select.dataset.maxlength) || 64,
|
||||
text: select.dataset.defaultValue || ''
|
||||
}
|
||||
this.$phoneAPI.getReponseText(options).then(data => {
|
||||
$input.value = data.text
|
||||
$input.dispatchEvent(new window.Event('change'))
|
||||
})
|
||||
}
|
||||
if (select.dataset.type === 'button') {
|
||||
select.click()
|
||||
}
|
||||
}
|
||||
},
|
||||
onBack () {
|
||||
if (this.state !== this.STATES.MENU) {
|
||||
this.state = this.STATES.MENU
|
||||
} else {
|
||||
this.$bus.$emit('twitterHome')
|
||||
}
|
||||
},
|
||||
setLocalAccount ($event, key) {
|
||||
this.localAccount[key] = $event.target.value
|
||||
},
|
||||
async setLocalAccountAvartar ($event) {
|
||||
try {
|
||||
const data = await Modal.CreateTextModal({
|
||||
text: this.twitterAvatarUrl || 'https://i.imgur.com/'
|
||||
})
|
||||
this.localAccount.avatarUrl = data.text
|
||||
} catch (e) {}
|
||||
},
|
||||
async onPressChangeAvartar () {
|
||||
try {
|
||||
const data = await Modal.CreateTextModal({
|
||||
text: this.twitterAvatarUrl || 'https://i.imgur.com/'
|
||||
})
|
||||
this.twitterSetAvatar({avatarUrl: data.text})
|
||||
} catch (e) {}
|
||||
},
|
||||
login () {
|
||||
this.twitterLogin({
|
||||
username: this.localAccount.username,
|
||||
password: this.localAccount.password
|
||||
})
|
||||
this.state = STATES.MENU
|
||||
},
|
||||
logout () {
|
||||
this.twitterLogout()
|
||||
},
|
||||
createAccount () {
|
||||
if (this.validAccount === true) {
|
||||
this.twitterCreateNewAccount(this.localAccount)
|
||||
this.localAccount = {
|
||||
username: '',
|
||||
password: '',
|
||||
passwordConfirm: '',
|
||||
avatarUrl: null
|
||||
}
|
||||
this.state = this.STATES.MENU
|
||||
}
|
||||
},
|
||||
cancel () {
|
||||
this.state = STATES.MENU
|
||||
},
|
||||
setNotification (value) {
|
||||
this.setTwitterNotification(value)
|
||||
},
|
||||
setNotificationSound (value) {
|
||||
this.setTwitterNotificationSound(value)
|
||||
},
|
||||
async changePassword (value) {
|
||||
try {
|
||||
const password1 = await Modal.CreateTextModal({limit: 30})
|
||||
if (password1.text === '') return
|
||||
const password2 = await Modal.CreateTextModal({limit: 30})
|
||||
if (password2.text === '') return
|
||||
if (password2.text !== password1.text) {
|
||||
this.$notify({
|
||||
title: this.IntlString('APP_TWITTER_NAME'),
|
||||
message: this.IntlString('APP_TWITTER_NOTIF_NEW_PASSWORD_MISS_MATCH'),
|
||||
icon: 'twitter',
|
||||
backgroundColor: '#e0245e80'
|
||||
})
|
||||
return
|
||||
} else if (password2.text.length < 6) {
|
||||
this.$notify({
|
||||
title: this.IntlString('APP_TWITTER_NAME'),
|
||||
message: this.IntlString('APP_TWITTER_NOTIF_NEW_PASSWORD_LENGTH_ERROR'),
|
||||
icon: 'twitter',
|
||||
backgroundColor: '#e0245e80'
|
||||
})
|
||||
return
|
||||
}
|
||||
this.twitterChangePassword(password2.text)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$on('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
this.$bus.$on('keyUpBackspace', this.onBack)
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$off('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
this.$bus.$off('keyUpBackspace', this.onBack)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.content{
|
||||
margin: 6px 10px;
|
||||
margin-top: 28px;
|
||||
height: calc(100% - 48px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.group {
|
||||
position:relative;
|
||||
margin-top:24px;
|
||||
}
|
||||
.group.inputText {
|
||||
position:relative;
|
||||
margin-top:45px;
|
||||
}
|
||||
|
||||
.group.bottom {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.group.img {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
.group.img img{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-grow: 0;
|
||||
flex: 0 0 128px;
|
||||
height: 128px;
|
||||
margin-right: 24px;
|
||||
}
|
||||
|
||||
input {
|
||||
font-size:24px;
|
||||
display:block;
|
||||
width: 314px;
|
||||
border:none;
|
||||
border-bottom:1px solid #757575;
|
||||
}
|
||||
input:focus { outline:none; }
|
||||
|
||||
/* LABEL ======================================= */
|
||||
.group.inputText label {
|
||||
color:#999;
|
||||
font-size:18px;
|
||||
font-weight:normal;
|
||||
position:absolute;
|
||||
pointer-events:none;
|
||||
left:5px;
|
||||
top:10px;
|
||||
transition:0.2s ease all;
|
||||
-moz-transition:0.2s ease all;
|
||||
-webkit-transition:0.2s ease all;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
display: flex;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
align-items: center;
|
||||
color: #007aff;
|
||||
font-weight: 200;
|
||||
border-radius: 6px;
|
||||
padding-left: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.checkbox input {
|
||||
width: 24px;
|
||||
height: 0px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.checkbox input::after {
|
||||
box-sizing: border-box;
|
||||
content: '';
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
margin-top: -10px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
background-color: white;
|
||||
border: 3px #007aff solid;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.checkbox input:checked::after {
|
||||
background-color: #007aff;
|
||||
}
|
||||
|
||||
.checkbox.select {
|
||||
border: 1px solid #007aff;
|
||||
background-color: #007aff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.groupCheckBoxTitle {
|
||||
font-weight: 700;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
/* active state */
|
||||
.group.inputText input:focus ~ label, .group.inputText input:valid ~ label {
|
||||
top:-24px;
|
||||
font-size:18px;
|
||||
color:#007aff;
|
||||
}
|
||||
|
||||
/* BOTTOM BARS ================================= */
|
||||
.bar { position:relative; display:block; width:100%; }
|
||||
.bar:before, .bar:after {
|
||||
content:'';
|
||||
height:2px;
|
||||
width:0;
|
||||
bottom:1px;
|
||||
position:absolute;
|
||||
background:#007aff;
|
||||
transition:0.2s ease all;
|
||||
-moz-transition:0.2s ease all;
|
||||
-webkit-transition:0.2s ease all;
|
||||
}
|
||||
.bar:before {
|
||||
left:50%;
|
||||
}
|
||||
.bar:after {
|
||||
right:50%;
|
||||
}
|
||||
|
||||
/* active state */
|
||||
input:focus ~ .bar:before, input:focus ~ .bar:after,
|
||||
.group.select input ~ .bar:before, .group.select input ~ .bar:after{
|
||||
width:50%;
|
||||
}
|
||||
|
||||
/* HIGHLIGHTER ================================== */
|
||||
.highlight {
|
||||
position:absolute;
|
||||
height:60%;
|
||||
width:100px;
|
||||
top:25%;
|
||||
left:0;
|
||||
pointer-events:none;
|
||||
opacity:0.5;
|
||||
}
|
||||
|
||||
/* active state */
|
||||
input:focus ~ .highlight {
|
||||
-webkit-animation:inputHighlighter 0.3s ease;
|
||||
-moz-animation:inputHighlighter 0.3s ease;
|
||||
animation:inputHighlighter 0.3s ease;
|
||||
}
|
||||
|
||||
.group .btn{
|
||||
width: 100%;
|
||||
padding: 0px 0px;
|
||||
height: 48px;
|
||||
color: #fff;
|
||||
border: 0 none;
|
||||
font-size: 22px;
|
||||
font-weight: 500;
|
||||
line-height: 34px;
|
||||
color: #202129;
|
||||
background-color: #edeeee;
|
||||
}
|
||||
.group.select .btn{
|
||||
/* border: 6px solid #C0C0C0; */
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.group .btn.btn-blue{
|
||||
width: 293px;
|
||||
margin-left: 6px;
|
||||
border: 1px solid #007aff;
|
||||
color: #007aff;
|
||||
background-color: white;
|
||||
font-weight: 500;
|
||||
border-radius: 10px;
|
||||
font-weight: 300;
|
||||
font-size: 19px;
|
||||
}
|
||||
.group.select .btn.btn-blue, .group:hover .btn.btn-blue{
|
||||
background-color: #007aff;
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.group .btn.btn-red{
|
||||
border: 1px solid #ee3838;
|
||||
color: #ee3838;
|
||||
background-color: white;
|
||||
font-weight: 200;
|
||||
border-radius: 10px;
|
||||
width: 193px;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 11px;
|
||||
}
|
||||
.group.select .btn.btn-red, .group:hover .btn.btn-red{
|
||||
background-color: #ee3838;
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.group .btn.btn-gray{
|
||||
border: none;
|
||||
color: #222;
|
||||
background-color: #AAA;
|
||||
font-weight: 500;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.group.select .btn.btn-gray, .group:hover .btn.btn-gray{
|
||||
background-color: #757575;
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* ANIMATIONS ================ */
|
||||
@-webkit-keyframes inputHighlighter {
|
||||
from { background:#007aff; }
|
||||
to { width:0; background:transparent; }
|
||||
}
|
||||
@-moz-keyframes inputHighlighter {
|
||||
from { background:#007aff; }
|
||||
to { width:0; background:transparent; }
|
||||
}
|
||||
@keyframes inputHighlighter {
|
||||
from { background:#007aff; }
|
||||
to { width:0; background:transparent; }
|
||||
}
|
||||
</style>
|
||||
120
HTML/gcphone/src/components/twitter/TwitterPostTweet.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<div style="width: 326px; height: 743px;" class="phone_content">
|
||||
<div class='tweet_write'>
|
||||
<textarea
|
||||
class="textarea-input"
|
||||
v-model.trim="message"
|
||||
v-autofocus
|
||||
:placeholder="IntlString('APP_TWITTER_PLACEHOLDER_MESSAGE')"
|
||||
></textarea>
|
||||
<span class='tweet_send' @click="tweeter">{{ IntlString('APP_TWITTER_BUTTON_ACTION_TWEETER') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data () {
|
||||
return {
|
||||
message: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'useMouse'])
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['twitterPostTweet']),
|
||||
async onEnter () {
|
||||
try {
|
||||
const rep = await this.$phoneAPI.getReponseText({
|
||||
// text: 'https://i.imgur.com/axLm3p6.png'
|
||||
})
|
||||
if (rep !== undefined && rep.text !== undefined) {
|
||||
const message = rep.text.trim()
|
||||
if (message.length !== 0) {
|
||||
this.twitterPostTweet({ message })
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
},
|
||||
async tweeter () {
|
||||
if (this.message === '') return
|
||||
await this.twitterPostTweet({ message: this.message })
|
||||
this.message = ''
|
||||
},
|
||||
onBack () {
|
||||
if (this.useMouse === true && document.activeElement.tagName !== 'BODY') return
|
||||
this.$bus.$emit('twitterHome')
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
}
|
||||
this.$bus.$on('keyUpBackspace', this.onBack)
|
||||
},
|
||||
async mounted () {
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpBackspace', this.onBack)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.phone_content {
|
||||
background: #DBF0F4;
|
||||
|
||||
}
|
||||
|
||||
.tweet_write{
|
||||
widows: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.tweet_write .textarea-input{
|
||||
align-self: center;
|
||||
width: 90%;
|
||||
margin-top: 20px;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: 16px;
|
||||
padding: 13px 16px;
|
||||
height: 336px;
|
||||
background-color: #ffffff;
|
||||
color: white;
|
||||
border-radius: 16px;
|
||||
resize: none;
|
||||
color: #222;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
|
||||
.tweet_send{
|
||||
align-self: flex-end;
|
||||
width: 120px;
|
||||
height: 32px;
|
||||
float: right;
|
||||
border-radius: 16px;
|
||||
background-color: rgb(29, 161, 242);
|
||||
margin-right: 12px;
|
||||
margin-bottom: 2px;
|
||||
color: white;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
margin: 26px 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
.tweet_send:hover {
|
||||
cursor: pointer;
|
||||
background-color: #0084b4;
|
||||
}
|
||||
</style>
|
||||
128
HTML/gcphone/src/components/twitter/TwitterScreen.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<div style="width: 326px; height: 743px;" class="phone_app">
|
||||
<PhoneTitle :title="currentScreen.title" backgroundColor="white" v-on:back="quit"/>
|
||||
<div class="phone_content">
|
||||
<component v-bind:is="currentScreen.component"/>
|
||||
</div>
|
||||
<div class="twitter_menu">
|
||||
<div
|
||||
v-for="(s, i) in screen"
|
||||
:key="i"
|
||||
class="twitter_menu-item"
|
||||
:class="{select: i === currentScreenIndex}"
|
||||
@click.stop="openMenu(i)">
|
||||
<i class="fa" :class="s.icon" @click.stop="openMenu(i)"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PhoneTitle from './../PhoneTitle'
|
||||
import TwitterView from './TwitterView'
|
||||
import TwitterPostTweet from './TwitterPostTweet'
|
||||
import TwitterAccount from './TwitterAccount'
|
||||
import TwitterTopTweet from './TwitterTopTweet'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PhoneTitle
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
currentScreenIndex: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['IntlString', 'useMouse']),
|
||||
screen () {
|
||||
return [
|
||||
{
|
||||
title: this.IntlString('APP_TWITTER_VIEW_TWITTER'),
|
||||
component: TwitterView,
|
||||
icon: 'fa-home'
|
||||
},
|
||||
{
|
||||
title: this.IntlString('APP_TWITTER_VIEW_TOP_TWEETS'),
|
||||
component: TwitterTopTweet,
|
||||
icon: 'fa-heart'
|
||||
},
|
||||
{
|
||||
title: this.IntlString('APP_TWITTER_VIEW_TWEETER'),
|
||||
component: TwitterPostTweet,
|
||||
icon: ' fa-comment-o'
|
||||
},
|
||||
{
|
||||
title: this.IntlString('APP_TWITTER_VIEW_SETTING'),
|
||||
component: TwitterAccount,
|
||||
icon: 'fa-cog'
|
||||
}
|
||||
]
|
||||
},
|
||||
currentScreen () {
|
||||
return this.screen[this.currentScreenIndex]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
methods: {
|
||||
onLeft () {
|
||||
this.currentScreenIndex = Math.max(0, this.currentScreenIndex - 1)
|
||||
},
|
||||
onRight () {
|
||||
this.currentScreenIndex = Math.min(this.screen.length - 1, this.currentScreenIndex + 1)
|
||||
},
|
||||
home () {
|
||||
this.currentScreenIndex = 0
|
||||
},
|
||||
openMenu (index) {
|
||||
this.currentScreenIndex = index
|
||||
},
|
||||
quit () {
|
||||
if (this.currentScreenIndex === 0) {
|
||||
this.$router.push({ name: 'home' })
|
||||
} else {
|
||||
this.currentScreenIndex = 0
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpArrowLeft', this.onLeft)
|
||||
this.$bus.$on('keyUpArrowRight', this.onRight)
|
||||
}
|
||||
this.$bus.$on('twitterHome', this.home)
|
||||
},
|
||||
mounted () {
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpArrowLeft', this.onLeft)
|
||||
this.$bus.$off('keyUpArrowRight', this.onRight)
|
||||
this.$bus.$off('twitterHome', this.home)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.twitter_menu {
|
||||
border-top: 1px solid #CCC;
|
||||
height: 56px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
.twitter_menu-item {
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #959595;
|
||||
}
|
||||
.twitter_menu-item.select {
|
||||
color: #1da1f2;
|
||||
}
|
||||
.twitter_menu-item:hover {
|
||||
color: #1da1f2;
|
||||
}
|
||||
</style>
|
||||
36
HTML/gcphone/src/components/twitter/TwitterSpashScreen.vue
Normal file
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div style="width: 326px; height: 743px;" class="splash">
|
||||
<img src="/html/static/img/twitter/bird.png">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
created: function () {
|
||||
setTimeout(() => {
|
||||
this.$router.push({ name: 'twitter.screen' })
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.splash{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: white; /*#1da1f2;*/
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
img {
|
||||
width: 80px;
|
||||
animation-name: zoom;
|
||||
animation-duration: 0.35s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
@keyframes zoom {
|
||||
from {width: 180px;}
|
||||
to {width: 250px;}
|
||||
}
|
||||
</style>
|
||||
379
HTML/gcphone/src/components/twitter/TwitterTopTweet.vue
Normal file
@@ -0,0 +1,379 @@
|
||||
<template>
|
||||
<div style="width: 326px; height: 743px;" class="phone_content">
|
||||
<div class="img-fullscreen" v-if="imgZoom !== undefined" @click.stop="imgZoom = undefined">
|
||||
<img :src="imgZoom" />
|
||||
</div>
|
||||
<div class="tweets-wrapper" ref="elementsDiv">
|
||||
<div class="tweet" v-for='(tweet, key) in tweets'
|
||||
v-bind:key="tweet.id"
|
||||
v-bind:class="{ select: key === selectMessage}"
|
||||
>
|
||||
<div class="tweet-img">
|
||||
<img :src="tweet.authorIcon || 'html/static/img/twitter/default_profile.png'" width="48" height="48"/>
|
||||
</div>
|
||||
<div class="tweet-content">
|
||||
<div class="tweet-head">
|
||||
<div class="tweet-head-author">{{ tweet.author }}</div>
|
||||
<div class="tweet-head-time">{{formatTime(tweet.time)}}</div>
|
||||
</div>
|
||||
<div class="tweet-message">
|
||||
<template v-if="!isImage(tweet.message)">{{ tweet.message }}</template>
|
||||
<img v-else :src="tweet.message" class="tweet-attachement-img" @click.stop="imgZoom = tweet.message">
|
||||
</div>
|
||||
<div class="tweet-like">
|
||||
|
||||
<div class="item svgreply" @click.stop="reply(tweet)">
|
||||
<svg @click.stop="reply(tweet)" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/></svg>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4z"/></svg>
|
||||
</div>
|
||||
|
||||
<div v-if="tweet.isLikes" class="item svgdislike" @click.stop="twitterToogleLike({ tweetId: tweet.id })">
|
||||
<svg @click.stop="twitterToogleLike({ tweetId: tweet.id })" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>
|
||||
<span @click.stop="twitterToogleLike({ tweetId: tweet.id })">{{ tweet.likes }}</span>
|
||||
</div>
|
||||
<div v-else class="svglike" @click.stop="twitterToogleLike({ tweetId: tweet.id })">
|
||||
<svg @click.stop="twitterToogleLike({ tweetId: tweet.id })" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M16.5 3c-1.74 0-3.41.81-4.5 2.09C10.91 3.81 9.24 3 7.5 3 4.42 3 2 5.42 2 8.5c0 3.78 3.4 6.86 8.55 11.54L12 21.35l1.45-1.32C18.6 15.36 22 12.28 22 8.5 22 5.42 19.58 3 16.5 3zm-4.4 15.55l-.1.1-.1-.1C7.14 14.24 4 11.39 4 8.5 4 6.5 5.5 5 7.5 5c1.54 0 3.04.99 3.57 2.36h1.87C13.46 5.99 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5 0 2.89-3.14 5.74-7.9 10.05z"/></svg>
|
||||
<span @click.stop="twitterToogleLike({ tweetId: tweet.id })">{{ tweet.likes }}</span>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92s2.92-1.31 2.92-2.92-1.31-2.92-2.92-2.92z"/></svg>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
import Modal from '@/components/Modal/index.js'
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data () {
|
||||
return {
|
||||
selectMessage: -1,
|
||||
ignoreControls: false,
|
||||
imgZoom: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['favoriteTweets', 'IntlString', 'useMouse']),
|
||||
tweets () {
|
||||
return this.favoriteTweets
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['twitterLogin', 'twitterPostTweet', 'twitterToogleLike', 'fetchFavoriteTweets']),
|
||||
async showOption () {
|
||||
this.ignoreControls = true
|
||||
const tweet = this.tweets[this.selectMessage]
|
||||
let optionsChoix = [{
|
||||
id: 1,
|
||||
title: 'Like / Unlike',
|
||||
icons: 'fa-heart'
|
||||
}, {
|
||||
id: 2,
|
||||
title: 'Répondre',
|
||||
icons: 'fa-reply'
|
||||
}, {
|
||||
id: -1,
|
||||
title: this.IntlString('CANCEL'),
|
||||
icons: 'fa-undo'
|
||||
}]
|
||||
if (this.isImage(tweet.message)) {
|
||||
optionsChoix = [{
|
||||
id: 3,
|
||||
title: this.IntlString('APP_MESSAGE_ZOOM_IMG'),
|
||||
icons: 'fa-search'
|
||||
}, ...optionsChoix]
|
||||
}
|
||||
const choix = await Modal.CreateModal({ choix: optionsChoix })
|
||||
this.ignoreControls = false
|
||||
switch (choix.id) {
|
||||
case 1:
|
||||
this.twitterToogleLike({ tweetId: tweet.id })
|
||||
break
|
||||
case 2:
|
||||
this.reply(tweet)
|
||||
break
|
||||
case 3:
|
||||
this.imgZoom = tweet.message
|
||||
break
|
||||
}
|
||||
},
|
||||
isImage (mess) {
|
||||
return /^https?:\/\/.*\.(png|jpg|jpeg|gif)/.test(mess)
|
||||
},
|
||||
async reply (tweet) {
|
||||
const authorName = tweet.author
|
||||
try {
|
||||
this.ignoreControls = true
|
||||
const rep = await Modal.CreateTextModal({
|
||||
title: 'Répondre',
|
||||
text: `@${authorName} `
|
||||
})
|
||||
if (rep !== undefined && rep.text !== undefined) {
|
||||
const message = rep.text.trim()
|
||||
if (message.length !== 0) {
|
||||
this.twitterPostTweet({ message })
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} finally {
|
||||
this.ignoreControls = false
|
||||
}
|
||||
},
|
||||
resetScroll () {
|
||||
this.$nextTick(() => {
|
||||
let elem = document.querySelector('#tweets')
|
||||
elem.scrollTop = elem.scrollHeight
|
||||
this.selectMessage = -1
|
||||
})
|
||||
},
|
||||
scrollIntoViewIfNeeded () {
|
||||
this.$nextTick(() => {
|
||||
const elem = this.$el.querySelector('.select')
|
||||
if (elem !== null) {
|
||||
elem.scrollIntoViewIfNeeded()
|
||||
}
|
||||
})
|
||||
},
|
||||
onUp: function () {
|
||||
if (this.ignoreControls === true) return
|
||||
if (this.selectMessage === -1) {
|
||||
this.selectMessage = 0
|
||||
} else {
|
||||
this.selectMessage = this.selectMessage === 0 ? 0 : this.selectMessage - 1
|
||||
}
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
onDown () {
|
||||
if (this.ignoreControls === true) return
|
||||
if (this.selectMessage === -1) {
|
||||
this.selectMessage = 0
|
||||
} else {
|
||||
this.selectMessage = this.selectMessage === this.tweets.length - 1 ? this.selectMessage : this.selectMessage + 1
|
||||
}
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
async onEnter () {
|
||||
if (this.ignoreControls === true) return
|
||||
if (this.selectMessage === -1) {
|
||||
this.newTweet()
|
||||
} else {
|
||||
this.showOption()
|
||||
}
|
||||
},
|
||||
onBack () {
|
||||
if (this.imgZoom !== undefined) {
|
||||
this.imgZoom = undefined
|
||||
return
|
||||
}
|
||||
if (this.ignoreControls === true) return
|
||||
if (this.selectMessage !== -1) {
|
||||
this.selectMessage = -1
|
||||
} else {
|
||||
this.$bus.$emit('twitterHome')
|
||||
}
|
||||
},
|
||||
formatTime (time) {
|
||||
const d = new Date(time)
|
||||
return d.toLocaleTimeString()
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$on('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
}
|
||||
this.$bus.$on('keyUpBackspace', this.onBack)
|
||||
},
|
||||
mounted () {
|
||||
this.fetchFavoriteTweets()
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$off('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
this.$bus.$off('keyUpBackspace', this.onBack)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.svgreply:hover {
|
||||
cursor: pointer;
|
||||
fill: #1da1f2;
|
||||
color: #1da1f2;
|
||||
}
|
||||
.svglike:hover {
|
||||
cursor: pointer;
|
||||
fill: red;
|
||||
color: red;
|
||||
}
|
||||
.svgdislike {
|
||||
fill: red;
|
||||
color: red;
|
||||
}
|
||||
.svgdislike:hover {
|
||||
cursor: pointer;
|
||||
fill: #C0C0C0;
|
||||
color: #C0C0C0;
|
||||
}
|
||||
|
||||
.img-fullscreen {
|
||||
position: fixed;
|
||||
z-index: 999999;
|
||||
background-color: rgba(20, 20, 20, 0.8);
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.img-fullscreen img {
|
||||
display: flex;
|
||||
max-width: 90vw;
|
||||
max-height: 95vh;
|
||||
}
|
||||
.tweets-wrapper{
|
||||
height: 100%;
|
||||
background-color: #DBF0F4;
|
||||
color: black;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.tweet{
|
||||
background-color: white;
|
||||
flex: 0 0 auto;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-bottom: #CCC 1px solid;
|
||||
padding-top: 6px;
|
||||
}
|
||||
.tweet.select {
|
||||
background-color: #c0deed;
|
||||
}
|
||||
|
||||
.tweet-img {
|
||||
width: 322px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tweet-img img{
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.tweet-content {
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
.tweet-head {
|
||||
padding-bottom: 4px;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.tweet-head-author {
|
||||
width: 100%;
|
||||
}
|
||||
.tweet-head-time {
|
||||
font-size: 12px;
|
||||
text-align: right;
|
||||
padding-right: 6px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.tweet-message{
|
||||
font-size: 14px;
|
||||
color: 000;
|
||||
min-height: 36px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.tweet-attachement-img {
|
||||
width: 96%;
|
||||
}
|
||||
|
||||
.tweet-like {
|
||||
margin-top: 6px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
font-size: 12px;
|
||||
line-height: 24px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.tweet-like div {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.tweet_write{
|
||||
height: 56px;
|
||||
widows: 100%;
|
||||
background: #c0deed;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
.tweet_write input{
|
||||
width: 75%;
|
||||
margin-left: 6%;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: 16px;
|
||||
padding: 3px 12px;
|
||||
float: left;
|
||||
height: 36px;
|
||||
background-color: #ffffff;
|
||||
color: white;
|
||||
border-radius: 16px;
|
||||
}
|
||||
.tweet_write input::placeholder {
|
||||
color: #888;
|
||||
}
|
||||
.tweet_send{
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
float: right;
|
||||
border-radius: 50%;
|
||||
background-color: #0084b4;
|
||||
margin-right: 12px;
|
||||
margin-bottom: 2px;
|
||||
color: white;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
.elements::-webkit-scrollbar-track
|
||||
{
|
||||
box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
||||
background-color: #a6a28c;
|
||||
}
|
||||
.elements::-webkit-scrollbar
|
||||
{
|
||||
width: 3px;
|
||||
background-color: transparent;
|
||||
}
|
||||
.elements::-webkit-scrollbar-thumb
|
||||
{
|
||||
background-color: #1da1f2;
|
||||
}
|
||||
</style>
|
||||
376
HTML/gcphone/src/components/twitter/TwitterView.vue
Normal file
@@ -0,0 +1,376 @@
|
||||
<template>
|
||||
<div style="width: 326px; height: 743px;" class="phone_content">
|
||||
<div class="img-fullscreen" v-if="imgZoom !== undefined" @click.stop="imgZoom = undefined">
|
||||
<img :src="imgZoom" />
|
||||
</div>
|
||||
<div class="tweets-wrapper" ref="elementsDiv">
|
||||
<div class="tweet" v-for='(tweet, key) in tweets'
|
||||
v-bind:key="tweet.id"
|
||||
v-bind:class="{ select: key === selectMessage}"
|
||||
>
|
||||
<div class="tweet-img">
|
||||
<img :src="tweet.authorIcon || 'html/static/img/twitter/default_profile.png'" width="48" height="48"/>
|
||||
</div>
|
||||
<div class="tweet-content">
|
||||
<div class="tweet-head">
|
||||
<div class="tweet-head-author">{{ tweet.author }}</div>
|
||||
<div class="tweet-head-time">{{formatTime(tweet.time)}}</div>
|
||||
</div>
|
||||
<div class="tweet-message">
|
||||
<template v-if="!isImage(tweet.message)">{{ tweet.message }}</template>
|
||||
<img v-else :src="tweet.message" class="tweet-attachement-img" @click.stop="imgZoom = tweet.message">
|
||||
</div>
|
||||
<div class="tweet-like">
|
||||
|
||||
<div class="item svgreply" @click.stop="reply(tweet)">
|
||||
<svg @click.stop="reply(tweet)" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/></svg>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4z"/></svg>
|
||||
</div>
|
||||
|
||||
<div v-if="tweet.isLikes" class="item svgdislike" @click.stop="twitterToogleLike({ tweetId: tweet.id })">
|
||||
<svg @click.stop="twitterToogleLike({ tweetId: tweet.id })" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>
|
||||
<span @click.stop="twitterToogleLike({ tweetId: tweet.id })">{{ tweet.likes }}</span>
|
||||
</div>
|
||||
<div v-else class="svglike" @click.stop="twitterToogleLike({ tweetId: tweet.id })">
|
||||
<svg @click.stop="twitterToogleLike({ tweetId: tweet.id })" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M16.5 3c-1.74 0-3.41.81-4.5 2.09C10.91 3.81 9.24 3 7.5 3 4.42 3 2 5.42 2 8.5c0 3.78 3.4 6.86 8.55 11.54L12 21.35l1.45-1.32C18.6 15.36 22 12.28 22 8.5 22 5.42 19.58 3 16.5 3zm-4.4 15.55l-.1.1-.1-.1C7.14 14.24 4 11.39 4 8.5 4 6.5 5.5 5 7.5 5c1.54 0 3.04.99 3.57 2.36h1.87C13.46 5.99 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5 0 2.89-3.14 5.74-7.9 10.05z"/></svg>
|
||||
<span @click.stop="twitterToogleLike({ tweetId: tweet.id })">{{ tweet.likes }}</span>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92s2.92-1.31 2.92-2.92-1.31-2.92-2.92-2.92z"/></svg>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
import Modal from '@/components/Modal/index.js'
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
data () {
|
||||
return {
|
||||
selectMessage: -1,
|
||||
ignoreControls: false,
|
||||
imgZoom: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['tweets', 'IntlString', 'useMouse'])
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['twitterLogin', 'twitterPostTweet', 'twitterToogleLike', 'fetchTweets']),
|
||||
async showOption () {
|
||||
this.ignoreControls = true
|
||||
const tweet = this.tweets[this.selectMessage]
|
||||
let optionsChoix = [{
|
||||
id: 1,
|
||||
title: 'Like / Unlike',
|
||||
icons: 'fa-heart'
|
||||
}, {
|
||||
id: 2,
|
||||
title: 'Répondre',
|
||||
icons: 'fa-reply'
|
||||
}, {
|
||||
id: -1,
|
||||
title: this.IntlString('CANCEL'),
|
||||
icons: 'fa-undo'
|
||||
}]
|
||||
if (this.isImage(tweet.message)) {
|
||||
optionsChoix = [{
|
||||
id: 3,
|
||||
title: this.IntlString('APP_MESSAGE_ZOOM_IMG'),
|
||||
icons: 'fa-search'
|
||||
}, ...optionsChoix]
|
||||
}
|
||||
const choix = await Modal.CreateModal({ choix: optionsChoix })
|
||||
this.ignoreControls = false
|
||||
switch (choix.id) {
|
||||
case 1:
|
||||
this.twitterToogleLike({ tweetId: tweet.id })
|
||||
break
|
||||
case 2:
|
||||
this.reply(tweet)
|
||||
break
|
||||
case 3:
|
||||
this.imgZoom = tweet.message
|
||||
break
|
||||
}
|
||||
},
|
||||
isImage (mess) {
|
||||
return /^https?:\/\/.*\.(png|jpg|jpeg|gif)/.test(mess)
|
||||
},
|
||||
async reply (tweet) {
|
||||
const authorName = tweet.author
|
||||
try {
|
||||
this.ignoreControls = true
|
||||
const rep = await Modal.CreateTextModal({
|
||||
title: 'Répondre',
|
||||
text: `@${authorName} `
|
||||
})
|
||||
if (rep !== undefined && rep.text !== undefined) {
|
||||
const message = rep.text.trim()
|
||||
if (message.length !== 0) {
|
||||
this.twitterPostTweet({ message })
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
} finally {
|
||||
this.ignoreControls = false
|
||||
}
|
||||
},
|
||||
resetScroll () {
|
||||
this.$nextTick(() => {
|
||||
let elem = document.querySelector('#tweets')
|
||||
elem.scrollTop = elem.scrollHeight
|
||||
this.selectMessage = -1
|
||||
})
|
||||
},
|
||||
scrollIntoViewIfNeeded () {
|
||||
this.$nextTick(() => {
|
||||
const elem = this.$el.querySelector('.select')
|
||||
if (elem !== null) {
|
||||
elem.scrollIntoViewIfNeeded()
|
||||
}
|
||||
})
|
||||
},
|
||||
onUp () {
|
||||
if (this.ignoreControls === true) return
|
||||
if (this.selectMessage === -1) {
|
||||
this.selectMessage = 0
|
||||
} else {
|
||||
this.selectMessage = this.selectMessage === 0 ? 0 : this.selectMessage - 1
|
||||
}
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
onDown () {
|
||||
if (this.ignoreControls === true) return
|
||||
if (this.selectMessage === -1) {
|
||||
this.selectMessage = 0
|
||||
} else {
|
||||
this.selectMessage = this.selectMessage === this.tweets.length - 1 ? this.selectMessage : this.selectMessage + 1
|
||||
}
|
||||
this.scrollIntoViewIfNeeded()
|
||||
},
|
||||
async onEnter () {
|
||||
if (this.ignoreControls === true) return
|
||||
if (this.selectMessage === -1) {
|
||||
this.newTweet()
|
||||
} else {
|
||||
this.showOption()
|
||||
}
|
||||
},
|
||||
onBack () {
|
||||
if (this.imgZoom !== undefined) {
|
||||
this.imgZoom = undefined
|
||||
return
|
||||
}
|
||||
if (this.ignoreControls === true) return
|
||||
if (this.selectMessage !== -1) {
|
||||
this.selectMessage = -1
|
||||
} else {
|
||||
this.$router.push({ name: 'home' })
|
||||
}
|
||||
},
|
||||
formatTime (time) {
|
||||
const d = new Date(time)
|
||||
return d.toLocaleTimeString()
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (!this.useMouse) {
|
||||
this.$bus.$on('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$on('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$on('keyUpEnter', this.onEnter)
|
||||
}
|
||||
this.$bus.$on('keyUpBackspace', this.onBack)
|
||||
},
|
||||
mounted () {
|
||||
this.fetchTweets()
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$bus.$off('keyUpArrowDown', this.onDown)
|
||||
this.$bus.$off('keyUpArrowUp', this.onUp)
|
||||
this.$bus.$off('keyUpEnter', this.onEnter)
|
||||
this.$bus.$off('keyUpBackspace', this.onBack)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.svgreply:hover {
|
||||
cursor: pointer;
|
||||
fill: #1da1f2;
|
||||
color: #1da1f2;
|
||||
}
|
||||
.svglike:hover {
|
||||
cursor: pointer;
|
||||
fill: red;
|
||||
color: red;
|
||||
}
|
||||
.svgdislike {
|
||||
fill: red;
|
||||
color: red;
|
||||
}
|
||||
.svgdislike:hover {
|
||||
cursor: pointer;
|
||||
fill: #C0C0C0;
|
||||
color: #C0C0C0;
|
||||
}
|
||||
|
||||
.img-fullscreen {
|
||||
position: fixed;
|
||||
z-index: 999999;
|
||||
background-color: rgba(20, 20, 20, 0.8);
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.img-fullscreen img {
|
||||
display: flex;
|
||||
max-width: 90vw;
|
||||
max-height: 95vh;
|
||||
}
|
||||
.tweets-wrapper{
|
||||
height: 100%;
|
||||
background-color: #DBF0F4;
|
||||
color: black;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.tweet{
|
||||
background-color: white;
|
||||
flex: 0 0 auto;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-bottom: #CCC 1px solid;
|
||||
padding-top: 6px;
|
||||
}
|
||||
.tweet.select {
|
||||
background-color: #c0deed;
|
||||
}
|
||||
|
||||
.tweet-img {
|
||||
width: 322px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tweet-img img{
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.tweet-content {
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
.tweet-head {
|
||||
padding-bottom: 4px;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.tweet-head-author {
|
||||
width: 100%;
|
||||
}
|
||||
.tweet-head-time {
|
||||
font-size: 12px;
|
||||
text-align: right;
|
||||
padding-right: 6px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.tweet-message{
|
||||
font-size: 14px;
|
||||
color: 000;
|
||||
min-height: 36px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.tweet-attachement-img {
|
||||
width: 96%;
|
||||
}
|
||||
|
||||
.tweet-like {
|
||||
margin-top: 6px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
font-size: 12px;
|
||||
line-height: 24px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.tweet-like div {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.tweet_write{
|
||||
height: 56px;
|
||||
widows: 100%;
|
||||
background: #c0deed;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
}
|
||||
.tweet_write input{
|
||||
width: 75%;
|
||||
margin-left: 6%;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: 16px;
|
||||
padding: 3px 12px;
|
||||
float: left;
|
||||
height: 36px;
|
||||
background-color: #ffffff;
|
||||
color: white;
|
||||
border-radius: 16px;
|
||||
}
|
||||
.tweet_write input::placeholder {
|
||||
color: #888;
|
||||
}
|
||||
.tweet_send{
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
float: right;
|
||||
border-radius: 50%;
|
||||
background-color: #0084b4;
|
||||
margin-right: 12px;
|
||||
margin-bottom: 2px;
|
||||
color: white;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
.elements::-webkit-scrollbar-track
|
||||
{
|
||||
box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
||||
background-color: #a6a28c;
|
||||
}
|
||||
.elements::-webkit-scrollbar
|
||||
{
|
||||
width: 3px;
|
||||
background-color: transparent;
|
||||
}
|
||||
.elements::-webkit-scrollbar-thumb
|
||||
{
|
||||
background-color: #1da1f2;
|
||||
}
|
||||
</style>
|
||||
7
HTML/gcphone/src/directives/autofocus.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const directive = {
|
||||
inserted (el) {
|
||||
el.focus()
|
||||
}
|
||||
}
|
||||
|
||||
export default directive
|
||||
202
HTML/gcphone/src/emoji.json
Normal file
@@ -0,0 +1,202 @@
|
||||
{
|
||||
"100":"💯",
|
||||
"1234":"🔢",
|
||||
"grinning":"😀",
|
||||
"grimacing":"😬",
|
||||
"grin":"😁",
|
||||
"joy":"😂",
|
||||
"rofl":"🤣",
|
||||
"partying":"🥳",
|
||||
"smiley":"😃",
|
||||
"smile":"😄",
|
||||
"sweat_smile":"😅",
|
||||
"laughing":"😆",
|
||||
"innocent":"😇",
|
||||
"wink":"😉",
|
||||
"blush":"😊",
|
||||
"slightly_smiling_face":"🙂",
|
||||
"upside_down_face":"🙃",
|
||||
"relaxed":"☺️",
|
||||
"yum":"😋",
|
||||
"relieved":"😌",
|
||||
"heart_eyes":"😍",
|
||||
"smiling_face_with_three_hearts":"🥰",
|
||||
"kissing_heart":"😘",
|
||||
"kissing":"😗",
|
||||
"kissing_smiling_eyes":"😙",
|
||||
"kissing_closed_eyes":"😚",
|
||||
"stuck_out_tongue_winking_eye":"😜",
|
||||
"zany":"🤪",
|
||||
"raised_eyebrow":"🤨",
|
||||
"monocle":"🧐",
|
||||
"stuck_out_tongue_closed_eyes":"😝",
|
||||
"stuck_out_tongue":"😛",
|
||||
"money_mouth_face":"🤑",
|
||||
"nerd_face":"🤓",
|
||||
"sunglasses":"😎",
|
||||
"star_struck":"🤩",
|
||||
"clown_face":"🤡",
|
||||
"cowboy_hat_face":"🤠",
|
||||
"hugs":"🤗",
|
||||
"smirk":"😏",
|
||||
"no_mouth":"😶",
|
||||
"neutral_face":"😐",
|
||||
"expressionless":"😑",
|
||||
"unamused":"😒",
|
||||
"roll_eyes":"🙄",
|
||||
"thinking":"🤔",
|
||||
"lying_face":"🤥",
|
||||
"hand_over_mouth":"🤭",
|
||||
"shushing":"🤫",
|
||||
"symbols_over_mouth":"🤬",
|
||||
"exploding_head":"🤯",
|
||||
"flushed":"😳",
|
||||
"disappointed":"😞",
|
||||
"worried":"😟",
|
||||
"angry":"😠",
|
||||
"rage":"😡",
|
||||
"pensive":"😔",
|
||||
"confused":"😕",
|
||||
"slightly_frowning_face":"🙁",
|
||||
"frowning_face":"☹",
|
||||
"persevere":"😣",
|
||||
"confounded":"😖",
|
||||
"tired_face":"😫",
|
||||
"weary":"😩",
|
||||
"pleading":"🥺",
|
||||
"triumph":"😤",
|
||||
"open_mouth":"😮",
|
||||
"scream":"😱",
|
||||
"fearful":"😨",
|
||||
"cold_sweat":"😰",
|
||||
"hushed":"😯",
|
||||
"frowning":"😦",
|
||||
"anguished":"😧",
|
||||
"cry":"😢",
|
||||
"disappointed_relieved":"😥",
|
||||
"drooling_face":"🤤",
|
||||
"sleepy":"😪",
|
||||
"sweat":"😓",
|
||||
"hot":"🥵",
|
||||
"cold":"🥶",
|
||||
"sob":"😭",
|
||||
"dizzy_face":"😵",
|
||||
"astonished":"😲",
|
||||
"zipper_mouth_face":"🤐",
|
||||
"nauseated_face":"🤢",
|
||||
"sneezing_face":"🤧",
|
||||
"vomiting":"🤮",
|
||||
"mask":"😷",
|
||||
"face_with_thermometer":"🤒",
|
||||
"face_with_head_bandage":"🤕",
|
||||
"woozy":"🥴",
|
||||
"sleeping":"😴",
|
||||
"zzz":"💤",
|
||||
"poop":"💩",
|
||||
"smiling_imp":"😈",
|
||||
"imp":"👿",
|
||||
"japanese_ogre":"👹",
|
||||
"japanese_goblin":"👺",
|
||||
"skull":"💀",
|
||||
"ghost":"👻",
|
||||
"alien":"👽",
|
||||
"robot":"🤖",
|
||||
"smiley_cat":"😺",
|
||||
"smile_cat":"😸",
|
||||
"joy_cat":"😹",
|
||||
"heart_eyes_cat":"😻",
|
||||
"smirk_cat":"😼",
|
||||
"kissing_cat":"😽",
|
||||
"scream_cat":"🙀",
|
||||
"crying_cat_face":"😿",
|
||||
"pouting_cat":"😾",
|
||||
"palms_up":"🤲",
|
||||
"raised_hands":"🙌",
|
||||
"clap":"👏",
|
||||
"wave":"👋",
|
||||
"call_me_hand":"🤙",
|
||||
"\\+1":"👍",
|
||||
"-1":"👎",
|
||||
"facepunch":"👊",
|
||||
"fist":"✊",
|
||||
"fist_left":"🤛",
|
||||
"fist_right":"🤜",
|
||||
"v":"✌",
|
||||
"ok_hand":"👌",
|
||||
"raised_hand":"✋",
|
||||
"raised_back_of_hand":"🤚",
|
||||
"open_hands":"👐",
|
||||
"muscle":"💪",
|
||||
"pray":"🙏",
|
||||
"foot":"🦶",
|
||||
"leg":"🦵",
|
||||
"handshake":"🤝",
|
||||
"point_up":"☝",
|
||||
"point_up_2":"👆",
|
||||
"point_down":"👇",
|
||||
"point_left":"👈",
|
||||
"point_right":"👉",
|
||||
"fu":"🖕",
|
||||
"raised_hand_with_fingers_splayed":"🖐",
|
||||
"love_you":"🤟",
|
||||
"metal":"🤘",
|
||||
"crossed_fingers":"🤞",
|
||||
"vulcan_salute":"🖖",
|
||||
"writing_hand":"✍",
|
||||
"selfie":"🤳",
|
||||
"nail_care":"💅",
|
||||
"lips":"👄",
|
||||
"tooth":"🦷",
|
||||
"tongue":"👅",
|
||||
"ear":"👂",
|
||||
"nose":"👃",
|
||||
"eye":"👁",
|
||||
"eyes":"👀",
|
||||
"brain":"🧠",
|
||||
"bust_in_silhouette":"👤",
|
||||
"busts_in_silhouette":"👥",
|
||||
"speaking_head":"🗣",
|
||||
"baby":"👶",
|
||||
"child":"🧒",
|
||||
"boy":"👦",
|
||||
"girl":"👧",
|
||||
"adult":"🧑",
|
||||
"man":"👨",
|
||||
"woman":"👩",
|
||||
"blonde_woman":"👱♀️",
|
||||
"blonde_man":"👱",
|
||||
"bearded_person":"🧔",
|
||||
"older_adult":"🧓",
|
||||
"older_man":"👴",
|
||||
"older_woman":"👵",
|
||||
"man_with_gua_pi_mao":"👲",
|
||||
"woman_with_headscarf":"🧕",
|
||||
"woman_with_turban":"👳♀️",
|
||||
"man_with_turban":"👳",
|
||||
"policewoman":"👮♀️",
|
||||
"policeman":"👮",
|
||||
"construction_worker_woman":"👷♀️",
|
||||
"construction_worker_man":"👷",
|
||||
"guardswoman":"💂♀️",
|
||||
"guardsman":"💂",
|
||||
"female_detective":"🕵️♀️",
|
||||
"male_detective":"🕵",
|
||||
"woman_health_worker":"👩⚕️",
|
||||
"man_health_worker":"👨⚕️",
|
||||
"woman_farmer":"👩🌾",
|
||||
"man_farmer":"👨🌾",
|
||||
"woman_cook":"👩🍳",
|
||||
"man_cook":"👨🍳",
|
||||
"woman_student":"👩🎓",
|
||||
"man_student":"👨🎓",
|
||||
"woman_singer":"👩🎤",
|
||||
"man_singer":"👨🎤",
|
||||
"woman_teacher":"👩🏫",
|
||||
"man_teacher":"👨🏫",
|
||||
"woman_factory_worker":"👩🏭",
|
||||
"man_factory_worker":"👨🏭",
|
||||
"woman_technologist":"👩💻",
|
||||
"man_technologist":"👨💻",
|
||||
"woman_office_worker":"👩💼",
|
||||
"man_office_worker":"👨💼"
|
||||
}
|
||||
30
HTML/gcphone/src/main.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import Vue from 'vue'
|
||||
import App from './App'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import VueTimeago from './TimeAgo'
|
||||
import PhoneAPI from './PhoneAPI'
|
||||
import Notification from './Notification'
|
||||
|
||||
import AutoFocus from './directives/autofocus'
|
||||
|
||||
Vue.use(VueTimeago)
|
||||
Vue.use(Notification)
|
||||
Vue.config.productionTip = false
|
||||
|
||||
Vue.prototype.$bus = new Vue()
|
||||
Vue.prototype.$phoneAPI = PhoneAPI
|
||||
|
||||
window.VueTimeago = VueTimeago
|
||||
window.Vue = Vue
|
||||
window.store = store
|
||||
|
||||
Vue.directive('autofocus', AutoFocus)
|
||||
|
||||
/* eslint-disable no-new */
|
||||
window.APP = new Vue({
|
||||
el: '#app',
|
||||
store,
|
||||
router,
|
||||
render: h => h(App)
|
||||
})
|
||||
137
HTML/gcphone/src/router/index.js
Normal file
@@ -0,0 +1,137 @@
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
|
||||
import Home from '@/components/Home'
|
||||
import Menu from '@/components/Menu'
|
||||
|
||||
import Contacts from '@/components/contacts/Contacts'
|
||||
import Contact from '@/components/contacts/Contact'
|
||||
|
||||
import MessagesList from '@/components/messages/MessagesList'
|
||||
import Messages from '@/components/messages/Messages'
|
||||
import MessageContactsSelect from '@/components/messages/MessageContactsSelect'
|
||||
|
||||
import Appels from '@/components/Appels/Appels'
|
||||
import AppelsActive from '@/components/Appels/AppelsActive'
|
||||
import AppelsNumber from '@/components/Appels/AppelsNumber'
|
||||
|
||||
import TchatSplashScreen from '@/components/Tchat/TchatSplashScreen'
|
||||
import TchatChannel from '@/components/Tchat/TchatChannel'
|
||||
import TchatMessage from '@/components/Tchat/TchatMessage'
|
||||
|
||||
import NotesChannel from '@/components/Notes/NotesChannel'
|
||||
import NotesMessage from '@/components/Notes/NotesMessage'
|
||||
|
||||
import TwitterSpashScreen from '@/components/twitter/TwitterSpashScreen'
|
||||
import TwitterScreen from '@/components/twitter/TwitterScreen'
|
||||
|
||||
import Parametre from '@/components/parametre/Parametre'
|
||||
import Bank from '@/components/Bank/Bank'
|
||||
import Bourse from '@/components/Bourse/Bourse'
|
||||
import Photo from '@/components/Photo/Photo'
|
||||
|
||||
import App9GAG from '@/components/App9GAG'
|
||||
Vue.use(Router)
|
||||
|
||||
export default new Router({
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: Home
|
||||
},
|
||||
{
|
||||
path: '/menu',
|
||||
name: 'menu',
|
||||
component: Menu
|
||||
},
|
||||
{
|
||||
path: '/contacts',
|
||||
name: 'contacts',
|
||||
component: Contacts
|
||||
},
|
||||
{
|
||||
path: '/contact/:id/:number?',
|
||||
name: 'contacts.view',
|
||||
component: Contact
|
||||
},
|
||||
{
|
||||
path: '/messages',
|
||||
name: 'messages',
|
||||
component: MessagesList
|
||||
},
|
||||
{
|
||||
path: '/messages/select',
|
||||
name: 'messages.selectcontact',
|
||||
component: MessageContactsSelect
|
||||
},
|
||||
{
|
||||
path: '/messages/:number/:display',
|
||||
name: 'messages.view',
|
||||
component: Messages
|
||||
}, {
|
||||
path: '/bourse',
|
||||
name: 'bourse',
|
||||
component: Bourse
|
||||
}, {
|
||||
path: '/bank',
|
||||
name: 'bank',
|
||||
component: Bank
|
||||
}, {
|
||||
path: '/photo',
|
||||
name: 'photo',
|
||||
component: Photo
|
||||
}, {
|
||||
path: '/paramtre',
|
||||
name: 'parametre',
|
||||
component: Parametre
|
||||
}, {
|
||||
path: '/appels',
|
||||
name: 'appels',
|
||||
component: Appels
|
||||
}, {
|
||||
path: '/appelsactive',
|
||||
name: 'appels.active',
|
||||
component: AppelsActive
|
||||
}, {
|
||||
path: '/appelsNumber',
|
||||
name: 'appels.number',
|
||||
component: AppelsNumber
|
||||
}, {
|
||||
path: '/tchatsplash',
|
||||
name: 'tchat',
|
||||
component: TchatSplashScreen
|
||||
}, {
|
||||
path: '/tchat',
|
||||
name: 'tchat.channel',
|
||||
component: TchatChannel
|
||||
}, {
|
||||
path: '/tchat/:channel',
|
||||
name: 'tchat.channel.show',
|
||||
component: TchatMessage
|
||||
}, {
|
||||
path: '/notes',
|
||||
name: 'notes',
|
||||
component: NotesChannel
|
||||
}, {
|
||||
path: '/notes/:channel',
|
||||
name: 'notes.channel.show',
|
||||
component: NotesMessage
|
||||
}, {
|
||||
path: '/twitter/splash',
|
||||
name: 'twitter.splash',
|
||||
component: TwitterSpashScreen
|
||||
}, {
|
||||
path: '/twitter/view',
|
||||
name: 'twitter.screen',
|
||||
component: TwitterScreen
|
||||
}, {
|
||||
path: '/9gag',
|
||||
name: '9gag',
|
||||
component: App9GAG
|
||||
}, {
|
||||
path: '*',
|
||||
redirect: '/'
|
||||
}
|
||||
]
|
||||
})
|
||||
29
HTML/gcphone/src/store/index.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
|
||||
import phone from './modules/phone'
|
||||
import contacts from './modules/contacts'
|
||||
import messages from './modules/messages'
|
||||
import appels from './modules/appels'
|
||||
import bank from './modules/bank'
|
||||
import notes from './modules/notes'
|
||||
import bourse from './modules/bourse'
|
||||
import tchat from './modules/tchat'
|
||||
import twitter from './modules/twitter'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
export default new Vuex.Store({
|
||||
modules: {
|
||||
phone,
|
||||
contacts,
|
||||
messages,
|
||||
appels,
|
||||
bank,
|
||||
bourse,
|
||||
notes,
|
||||
tchat,
|
||||
twitter
|
||||
},
|
||||
strict: true
|
||||
})
|
||||
116
HTML/gcphone/src/store/modules/appels.js
Normal file
@@ -0,0 +1,116 @@
|
||||
import PhoneAPI from './../../PhoneAPI'
|
||||
|
||||
const state = {
|
||||
appelsHistorique: [],
|
||||
appelsInfo: null
|
||||
}
|
||||
|
||||
const getters = {
|
||||
appelsHistorique: ({ appelsHistorique }) => appelsHistorique,
|
||||
appelsInfo: ({ appelsInfo }) => appelsInfo,
|
||||
appelsDisplayName (state, getters) {
|
||||
if (state.appelsInfo === null) {
|
||||
return 'ERROR'
|
||||
}
|
||||
if (state.appelsInfo.hidden === true) {
|
||||
return getters.IntlString('APP_PHONE_NUMBER_HIDDEN')
|
||||
}
|
||||
const num = getters.appelsDisplayNumber
|
||||
const contact = getters.contacts.find(e => e.number === num) || {}
|
||||
return contact.display || getters.IntlString('APP_PHONE_NUMBER_UNKNOWN')
|
||||
},
|
||||
appelsDisplayNumber (state, getters) {
|
||||
if (state.appelsInfo === null) {
|
||||
return 'ERROR'
|
||||
}
|
||||
if (getters.isInitiatorCall === true) {
|
||||
return state.appelsInfo.receiver_num
|
||||
}
|
||||
if (state.appelsInfo.hidden === true) {
|
||||
return '###-####'
|
||||
}
|
||||
return state.appelsInfo.transmitter_num
|
||||
},
|
||||
isInitiatorCall (state, getters) {
|
||||
if (state.appelsInfo === null) {
|
||||
return false
|
||||
}
|
||||
return state.appelsInfo.initiator === true
|
||||
}
|
||||
}
|
||||
|
||||
const actions = {
|
||||
startCall ({ commit }, { numero }) {
|
||||
PhoneAPI.startCall(numero)
|
||||
},
|
||||
acceptCall ({ state }) {
|
||||
PhoneAPI.acceptCall(state.appelsInfo)
|
||||
},
|
||||
rejectCall ({ state }) {
|
||||
PhoneAPI.rejectCall(state.appelsInfo)
|
||||
},
|
||||
ignoreCall ({ commit }) {
|
||||
commit('SET_APPELS_INFO', null)
|
||||
// PhoneAPI.ignoreCall(state.appelsInfo)
|
||||
},
|
||||
appelsDeleteHistorique ({ commit, state }, { numero }) {
|
||||
PhoneAPI.appelsDeleteHistorique(numero)
|
||||
commit('SET_APPELS_HISTORIQUE', state.appelsHistorique.filter(h => {
|
||||
return h.num !== numero
|
||||
}))
|
||||
},
|
||||
appelsDeleteAllHistorique ({ commit }) {
|
||||
PhoneAPI.appelsDeleteAllHistorique()
|
||||
commit('SET_APPELS_HISTORIQUE', [])
|
||||
},
|
||||
resetAppels ({ commit }) {
|
||||
commit('SET_APPELS_HISTORIQUE', [])
|
||||
commit('SET_APPELS_INFO', null)
|
||||
}
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
SET_APPELS_HISTORIQUE (state, appelsHistorique) {
|
||||
state.appelsHistorique = appelsHistorique
|
||||
},
|
||||
SET_APPELS_INFO_IF_EMPTY (state, appelsInfo) {
|
||||
if (state.appelsInfo === null) {
|
||||
state.appelsInfo = appelsInfo
|
||||
}
|
||||
},
|
||||
SET_APPELS_INFO (state, appelsInfo) {
|
||||
state.appelsInfo = appelsInfo
|
||||
},
|
||||
SET_APPELS_INFO_IS_ACCEPTS (state, isAccepts) {
|
||||
if (state.appelsInfo !== null) {
|
||||
state.appelsInfo = Object.assign({}, state.appelsInfo, {
|
||||
is_accepts: isAccepts
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// eslint-disable-next-line
|
||||
state.appelsHistorique = [{"id":1,"incoming":0,"num":"336-4557","owner":"336-4557","accepts":0,"time":1528374759000},{"id":2,"incoming":0,"num":"police","owner":"336-4557","accepts":1,"time":1528374787000},{"id":3,"incoming":1,"num":"555-5555","owner":"336-4557","accepts":1,"time":1528374566000},{"id":4,"incoming":1,"num":"555-5555","owner":"336-4557","accepts":0,"time":1528371227000}]
|
||||
state.appelsInfo = {
|
||||
initiator: false,
|
||||
id: 5,
|
||||
transmitter_src: 5,
|
||||
// transmitter_num: '###-####',
|
||||
transmitter_num: '336-4557',
|
||||
receiver_src: undefined,
|
||||
// receiver_num: '336-4557',
|
||||
receiver_num: '###-####',
|
||||
is_valid: 0,
|
||||
is_accepts: 0,
|
||||
hidden: 0
|
||||
}
|
||||
}
|
||||
29
HTML/gcphone/src/store/modules/bank.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import PhoneAPI from './../../PhoneAPI'
|
||||
|
||||
const state = {
|
||||
bankAmont: '0'
|
||||
}
|
||||
|
||||
const getters = {
|
||||
bankAmont: ({ bankAmont }) => bankAmont
|
||||
}
|
||||
|
||||
const actions = {
|
||||
sendpara ({ state }, { id, amount }) {
|
||||
PhoneAPI.callEvent('gcphone:bankTransfer', {id, amount})
|
||||
}
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
SET_BANK_AMONT (state, bankAmont) {
|
||||
state.bankAmont = bankAmont
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations
|
||||
}
|
||||
|
||||
43
HTML/gcphone/src/store/modules/bourse.js
Normal file
@@ -0,0 +1,43 @@
|
||||
const state = {
|
||||
bourseInfo: []
|
||||
}
|
||||
|
||||
const getters = {
|
||||
bourseInfo: ({ bourseInfo }) => bourseInfo
|
||||
}
|
||||
|
||||
const actions = {
|
||||
resetBourse ({ commit }) {
|
||||
commit('SET_BOURSE_INFO', [])
|
||||
}
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
SET_BOURSE_INFO (state, bourseInfo) {
|
||||
state.bourseInfo = bourseInfo
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// eslint-disable-next-line
|
||||
state.bourseInfo = [{
|
||||
difference: 0,
|
||||
libelle: 'Diamante',
|
||||
price: 1540.2
|
||||
}, {
|
||||
difference: 20,
|
||||
libelle: 'Hierro',
|
||||
price: 54.2
|
||||
}, {
|
||||
difference: -20.5,
|
||||
libelle: 'Cobre',
|
||||
price: 254.2
|
||||
}]
|
||||
}
|
||||
55
HTML/gcphone/src/store/modules/contacts.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import PhoneAPI from './../../PhoneAPI'
|
||||
|
||||
const state = {
|
||||
contacts: [],
|
||||
defaultContacts: []
|
||||
}
|
||||
|
||||
const getters = {
|
||||
contacts: ({ contacts, defaultContacts }) => [...contacts, ...defaultContacts]
|
||||
}
|
||||
|
||||
const actions = {
|
||||
updateContact (context, {id, display, number}) {
|
||||
PhoneAPI.updateContact(id, display, number)
|
||||
},
|
||||
addContact (context, {display, number}) {
|
||||
PhoneAPI.addContact(display, number)
|
||||
},
|
||||
deleteContact (context, {id}) {
|
||||
PhoneAPI.deleteContact(id)
|
||||
},
|
||||
resetContact ({ commit }) {
|
||||
commit('SET_CONTACTS', [])
|
||||
}
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
SET_CONTACTS (state, contacts) {
|
||||
state.contacts = contacts.sort((a, b) => a.display.localeCompare(b.display))
|
||||
},
|
||||
SET_DEFAULT_CONTACTS (state, contacts) {
|
||||
state.defaultContacts = contacts
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// eslint-disable-next-line
|
||||
state.contacts = [{
|
||||
id: 2,
|
||||
number: '336-4557',
|
||||
display: 'John doe'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
number: '336-4553',
|
||||
display: 'Nop user'
|
||||
}]
|
||||
}
|
||||
104
HTML/gcphone/src/store/modules/messages.js
Normal file
@@ -0,0 +1,104 @@
|
||||
import PhoneAPI from './../../PhoneAPI'
|
||||
|
||||
const state = {
|
||||
messages: []
|
||||
}
|
||||
|
||||
const getters = {
|
||||
messages: ({ messages }) => messages,
|
||||
nbMessagesUnread: ({ messages }) => {
|
||||
return messages.filter(e => e.isRead !== 1).length
|
||||
}
|
||||
}
|
||||
|
||||
const actions = {
|
||||
setMessages ({ commit }, messages) {
|
||||
commit('SET_MESSAGES', messages)
|
||||
},
|
||||
sendMessage ({ commit }, {phoneNumber, message}) {
|
||||
PhoneAPI.sendMessage(phoneNumber, message)
|
||||
},
|
||||
deleteMessage ({ commit }, { id }) {
|
||||
PhoneAPI.deleteMessage(id)
|
||||
},
|
||||
deleteMessagesNumber ({ commit, state }, { num }) {
|
||||
PhoneAPI.deleteMessagesNumber(num)
|
||||
commit('SET_MESSAGES', state.messages.filter(mess => {
|
||||
return mess.transmitter !== num
|
||||
}))
|
||||
},
|
||||
deleteAllMessages ({ commit }) {
|
||||
PhoneAPI.deleteAllMessages()
|
||||
commit('SET_MESSAGES', [])
|
||||
},
|
||||
setMessageRead ({ commit }, num) {
|
||||
PhoneAPI.setMessageRead(num)
|
||||
commit('SET_MESSAGES_READ', { num })
|
||||
},
|
||||
resetMessage ({ commit }) {
|
||||
commit('SET_MESSAGES', [])
|
||||
}
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
SET_MESSAGES (state, messages) {
|
||||
state.messages = messages
|
||||
},
|
||||
ADD_MESSAGE (state, message) {
|
||||
state.messages.push(message)
|
||||
},
|
||||
SET_MESSAGES_READ (state, { num }) {
|
||||
for (let i = 0; i < state.messages.length; i += 1) {
|
||||
if (state.messages[i].transmitter === num && state.messages[i].isRead !== 1) {
|
||||
state.messages[i].isRead = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
const time = new Date().getTime()
|
||||
const numRandom = '' + Math.floor(Math.random() * 10000000)
|
||||
state.messages = [
|
||||
{id: 0, transmitter: '0000', receiver: '06', time: time - 160, message: '#666-123', isRead: 1, owner: 0},
|
||||
{id: 1, transmitter: numRandom, receiver: '06', time: time - 160, message: 'Salut sa va ?!!!', isRead: 1, owner: 0},
|
||||
{id: 2, transmitter: numRandom, time, message: 'Tu fait quoi?', isRead: 1, owner: 0},
|
||||
{id: 3, transmitter: numRandom, time, message: 'Oui est toi ?', isRead: 1, owner: 1},
|
||||
{id: 4, transmitter: numRandom, time, message: 'GPS : 244 - 123', isRead: 1, owner: 0},
|
||||
{id: 2, transmitter: numRandom, time, message: 'Tu fait quoi?', isRead: 1, owner: 0},
|
||||
{id: 3, transmitter: numRandom, time, message: 'Oui est toi ?', isRead: 1, owner: 1},
|
||||
{id: 4, transmitter: numRandom, time, message: 'GPS : 244 - 123', isRead: 1, owner: 0},
|
||||
{id: 2, transmitter: numRandom, time, message: 'Tu fait quoi?', isRead: 1, owner: 0},
|
||||
{id: 3, transmitter: numRandom, time, message: 'Oui est toi ?', isRead: 1, owner: 1},
|
||||
{id: 4, transmitter: numRandom, time, message: 'GPS : 244 - 123', isRead: 1, owner: 0},
|
||||
{id: 2, transmitter: numRandom, time, message: 'Tu fait quoi?', isRead: 1, owner: 0},
|
||||
{id: 3, transmitter: numRandom, time, message: 'Oui est toi ?', isRead: 1, owner: 1},
|
||||
{id: 4, transmitter: numRandom, time, message: 'GPS : 244 - 123', isRead: 1, owner: 0},
|
||||
{id: 2, transmitter: numRandom, time, message: 'Tu fait quoi?', isRead: 1, owner: 0},
|
||||
{id: 3, transmitter: numRandom, time, message: 'Oui est toi ?', isRead: 1, owner: 1},
|
||||
{id: 4, transmitter: numRandom, time, message: 'GPS : 244 - 123', isRead: 1, owner: 0},
|
||||
{id: 2, transmitter: numRandom, time, message: 'Tu fait quoi?', isRead: 1, owner: 0},
|
||||
{id: 3, transmitter: numRandom, time, message: 'Oui est toi ?', isRead: 1, owner: 1},
|
||||
{id: 4, transmitter: numRandom, time, message: 'GPS : 244 - 123', isRead: 1, owner: 0},
|
||||
{id: 2, transmitter: numRandom, time, message: 'Tu fait quoi?', isRead: 1, owner: 0},
|
||||
{id: 3, transmitter: numRandom, time, message: 'Oui est toi ?', isRead: 1, owner: 1},
|
||||
{id: 4, transmitter: numRandom, time, message: 'GPS: 244.21, -123.15', isRead: 1, owner: 0},
|
||||
{id: 5, transmitter: 'police', time, message: 'Tu fait quoi?', isRead: 1, owner: 1},
|
||||
{id: 6, transmitter: 'ambulance', time, message: 'Oui est toi ?', isRead: 1, owner: 1},
|
||||
{id: 7, transmitter: '01', time, message: 'Salut sa va ?', isRead: 1, owner: 0},
|
||||
{id: 8, transmitter: '01', time, message: 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', isRead: 0, owner: 1},
|
||||
{id: 9, transmitter: '01', time, message: 'GPS: -1034.5810546875, -2734.1027832031', isRead: 1, owner: 0},
|
||||
{id: 44, transmitter: '01', time, message: 'https://i.imgur.com/gthahbs.png', isRead: 1, owner: 0},
|
||||
{id: 10, transmitter: '02', time, message: 'Salut sa va ?', isRead: 1, owner: 0},
|
||||
{id: 11, transmitter: '04', time, message: 'Salut sa va ?', isRead: 1, owner: 0},
|
||||
{id: 12, transmitter: '04', time, message: 'Salut sa va ?', isRead: 1, owner: 0},
|
||||
{id: 13, transmitter: '09', time, message: 'Tu sais pas !', isRead: 1, owner: 0}
|
||||
]
|
||||
}
|
||||
116
HTML/gcphone/src/store/modules/notes.js
Normal file
@@ -0,0 +1,116 @@
|
||||
import PhoneAPI from './../../PhoneAPI'
|
||||
const LOCAL_NAME = 'gc_notes_channels'
|
||||
|
||||
let NotesAudio = null
|
||||
|
||||
const state = {
|
||||
channels: JSON.parse(localStorage[LOCAL_NAME] || null) || [],
|
||||
currentChannel: null,
|
||||
messagesChannel: []
|
||||
}
|
||||
|
||||
const getters = {
|
||||
notesChannels: ({ channels }) => channels,
|
||||
notesCurrentChannel: ({ currentChannel }) => currentChannel,
|
||||
notesMessages: ({ messagesChannel }) => messagesChannel
|
||||
}
|
||||
|
||||
const actions = {
|
||||
notesReset ({commit}) {
|
||||
commit('NOTES_SET_MESSAGES', { messages: [] })
|
||||
commit('NOTES_SET_CHANNEL', { channel: null })
|
||||
commit('NOTES_REMOVES_ALL_CHANNELS')
|
||||
},
|
||||
notesSetChannel ({ state, commit, dispatch }, { channel }) {
|
||||
if (state.currentChannel !== channel) {
|
||||
commit('NOTES_SET_MESSAGES', { messages: [] })
|
||||
commit('NOTES_SET_CHANNEL', { channel })
|
||||
dispatch('notesGetMessagesChannel', { channel })
|
||||
}
|
||||
},
|
||||
notesAddMessage ({ state, commit, getters }, { message }) {
|
||||
const channel = message.channel
|
||||
if (state.channels.find(e => e.channel === channel) !== undefined) {
|
||||
if (NotesAudio !== null) {
|
||||
NotesAudio.pause()
|
||||
NotesAudio = null
|
||||
}
|
||||
NotesAudio = new Audio('/html/static/sound/tchatNotification.ogg')
|
||||
NotesAudio.volume = getters.volume
|
||||
NotesAudio.play()
|
||||
}
|
||||
commit('NOTES_ADD_MESSAGES', { message })
|
||||
},
|
||||
notesAddChannel ({ commit }, { channel }) {
|
||||
commit('NOTES_ADD_CHANNELS', { channel })
|
||||
},
|
||||
notesRemoveChannel ({ commit }, { channel }) {
|
||||
commit('NOTES_REMOVES_CHANNELS', { channel })
|
||||
},
|
||||
notesGetMessagesChannel ({ commit }, { channel }) {
|
||||
PhoneAPI.notesGetMessagesChannel(channel)
|
||||
},
|
||||
notesSendMessage (state, { channel, message }) {
|
||||
PhoneAPI.notesSendMessage(channel, message)
|
||||
}
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
NOTES_SET_CHANNEL (state, { channel }) {
|
||||
state.currentChannel = channel
|
||||
},
|
||||
NOTES_ADD_CHANNELS (state, { channel }) {
|
||||
state.channels.push({
|
||||
channel
|
||||
})
|
||||
localStorage[LOCAL_NAME] = JSON.stringify(state.channels)
|
||||
},
|
||||
NOTES_REMOVES_CHANNELS (state, { channel }) {
|
||||
state.channels = state.channels.filter(c => {
|
||||
return c.channel !== channel
|
||||
})
|
||||
localStorage[LOCAL_NAME] = JSON.stringify(state.channels)
|
||||
},
|
||||
NOTES_REMOVES_ALL_CHANNELS (state) {
|
||||
state.channels = []
|
||||
localStorage[LOCAL_NAME] = JSON.stringify(state.channels)
|
||||
},
|
||||
NOTES_ADD_MESSAGES (state, { message }) {
|
||||
if (message.channel === state.currentChannel) {
|
||||
state.messagesChannel.push(message)
|
||||
}
|
||||
},
|
||||
NOTES_SET_MESSAGES (state, { messages }) {
|
||||
state.messagesChannel = messages
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
state.currentChannel = 'debug'
|
||||
state.messagesChannel = JSON.parse('[{"channel":"teste","message":"teste","id":6,"time":1528671680000},{"channel":"teste","message":"Hop","id":5,"time":1528671153000}]')
|
||||
for (let i = 0; i < 200; i++) {
|
||||
state.messagesChannel.push(Object.assign({}, state.messagesChannel[0], { id: 100 + i, message: 'mess ' + i }))
|
||||
}
|
||||
state.messagesChannel.push({
|
||||
message: 'Message sur plusieur ligne car il faut bien !!! Ok !',
|
||||
id: 5000,
|
||||
time: new Date().getTime()
|
||||
})
|
||||
state.messagesChannel.push({
|
||||
message: 'Message sur plusieur ligne car il faut bien !!! Ok !',
|
||||
id: 5000,
|
||||
time: new Date().getTime()
|
||||
})
|
||||
state.messagesChannel.push({
|
||||
message: 'Message sur plusieur ligne car il faut bien !!! Ok !',
|
||||
id: 5000,
|
||||
time: new Date(4567845).getTime()
|
||||
})
|
||||
}
|
||||
229
HTML/gcphone/src/store/modules/phone.js
Normal file
@@ -0,0 +1,229 @@
|
||||
import Vue from 'vue'
|
||||
import PhoneAPI from './../../PhoneAPI'
|
||||
|
||||
const state = {
|
||||
show: process.env.NODE_ENV !== 'production',
|
||||
tempoHide: false,
|
||||
myPhoneNumber: '###-####',
|
||||
background: JSON.parse(window.localStorage['gc_background'] || null),
|
||||
coque: JSON.parse(window.localStorage['gc_coque'] || null),
|
||||
sonido: JSON.parse(window.localStorage['gc_sonido'] || null),
|
||||
zoom: window.localStorage['gc_zoom'] || '100%',
|
||||
volume: parseFloat(window.localStorage['gc_volume']) || 1,
|
||||
mouse: window.localStorage['gc_mouse'] === 'true',
|
||||
lang: window.localStorage['gc_language'],
|
||||
config: {
|
||||
reseau: 'ElBichop',
|
||||
useFormatNumberFrance: false,
|
||||
apps: [],
|
||||
themeColor: '#2A56C6',
|
||||
colors: ['#2A56C6'],
|
||||
language: {}
|
||||
}
|
||||
}
|
||||
|
||||
PhoneAPI.setUseMouse(state.mouse)
|
||||
|
||||
const getters = {
|
||||
show: ({ show }) => show,
|
||||
tempoHide: ({ tempoHide }) => tempoHide,
|
||||
myPhoneNumber: ({ myPhoneNumber }) => myPhoneNumber,
|
||||
volume: ({ volume }) => volume,
|
||||
enableTakePhoto: ({ config }) => config.enableTakePhoto === true,
|
||||
background: ({ background, config }) => {
|
||||
if (background === null) {
|
||||
if (config.background_default !== undefined) {
|
||||
return config.background_default
|
||||
}
|
||||
return {
|
||||
label: 'Default',
|
||||
value: 'default.jpg'
|
||||
}
|
||||
}
|
||||
return background
|
||||
},
|
||||
backgroundLabel: (state, getters) => getters.background.label,
|
||||
backgroundURL: (state, getters) => {
|
||||
if (getters.background.value.startsWith('http') === true) {
|
||||
return getters.background.value
|
||||
}
|
||||
return '/html/static/img/background/' + getters.background.value
|
||||
},
|
||||
coque: ({ coque, config }) => {
|
||||
if (coque === null) {
|
||||
if (config && config.coque_default !== undefined) {
|
||||
return config.coque_default
|
||||
}
|
||||
return {
|
||||
label: 'base',
|
||||
value: 'base.jpg'
|
||||
}
|
||||
}
|
||||
return coque
|
||||
},
|
||||
sonido: ({ sonido, config }) => {
|
||||
if (sonido === null) {
|
||||
if (config && config.sonido_default !== undefined) {
|
||||
return config.sonido_default
|
||||
}
|
||||
return {
|
||||
label: 'Panters',
|
||||
value: 'ring.ogg'
|
||||
}
|
||||
}
|
||||
return sonido
|
||||
},
|
||||
coqueLabel: (state, getters) => getters.coque.label,
|
||||
sonidoLabel: (state, getters) => getters.sonido.label,
|
||||
zoom: ({ zoom }) => zoom,
|
||||
useMouse: ({ mouse }) => mouse,
|
||||
config: ({ config }) => config,
|
||||
warningMessageCount: ({ config }) => config.warningMessageCount || 250,
|
||||
useFormatNumberFrance: ({ config }) => config.useFormatNumberFrance,
|
||||
themeColor: ({ config }) => config.themeColor,
|
||||
colors: ({ config }) => config.colors,
|
||||
Apps: ({ config, lang }, getters) => config.apps
|
||||
.filter(app => app.enabled !== false)
|
||||
.map(app => {
|
||||
if (app.puceRef !== undefined) {
|
||||
app.puce = getters[app.puceRef]
|
||||
}
|
||||
const keyName = `${lang}__name`
|
||||
app.intlName = app[keyName] || app.name
|
||||
return app
|
||||
}),
|
||||
AppsHome: (state, getters) => getters.Apps.filter(app => app.inHomePage === true),
|
||||
availableLanguages ({ config }) {
|
||||
const langKey = Object.keys(config.language)
|
||||
const AvailableLanguage = {}
|
||||
for (const key of langKey) {
|
||||
AvailableLanguage[config.language[key].NAME] = key
|
||||
}
|
||||
return AvailableLanguage
|
||||
},
|
||||
IntlString ({ config, lang }) {
|
||||
lang = lang || config.defaultLanguage
|
||||
if (config.language[lang] === undefined) {
|
||||
return (LABEL) => LABEL
|
||||
}
|
||||
return (LABEL, defaultValue) => {
|
||||
return config.language[lang][LABEL] || defaultValue || LABEL
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const actions = {
|
||||
async loadConfig ({ commit, state }) {
|
||||
const config = await PhoneAPI.getConfig()
|
||||
const keyLang = Object.keys(config.language)
|
||||
for (const key of keyLang) {
|
||||
const timeAgoConf = config.language[key].TIMEAGO
|
||||
if (timeAgoConf !== undefined) {
|
||||
Vue.prototype.$timeago.addLocale(key, timeAgoConf)
|
||||
}
|
||||
}
|
||||
Vue.prototype.$timeago.setCurrentLocale(state.lang)
|
||||
if (config.defaultContacts !== undefined) {
|
||||
commit('SET_DEFAULT_CONTACTS', config.defaultContacts)
|
||||
}
|
||||
commit('SET_CONFIG', config)
|
||||
},
|
||||
setEnableApp ({ commit, state }, { appName, enable = true }) {
|
||||
commit('SET_APP_ENABLE', { appName, enable })
|
||||
},
|
||||
setVisibility ({ commit }, show) {
|
||||
commit('SET_PHONE_VISIBILITY', show)
|
||||
},
|
||||
setZoon ({ commit }, zoom) {
|
||||
window.localStorage['gc_zoom'] = zoom
|
||||
commit('SET_ZOOM', zoom)
|
||||
},
|
||||
setBackground ({ commit }, background) {
|
||||
window.localStorage['gc_background'] = JSON.stringify(background)
|
||||
commit('SET_BACKGROUND', background)
|
||||
},
|
||||
setCoque ({ commit }, coque) {
|
||||
window.localStorage['gc_coque'] = JSON.stringify(coque)
|
||||
commit('SET_COQUE', coque)
|
||||
},
|
||||
setSonido ({ commit }, sonido) {
|
||||
window.localStorage['gc_sonido'] = JSON.stringify(sonido)
|
||||
commit('SET_SONIDO', sonido)
|
||||
},
|
||||
setVolume ({ commit }, volume) {
|
||||
window.localStorage['gc_volume'] = volume
|
||||
commit('SET_VOLUME', volume)
|
||||
},
|
||||
setLanguage ({ commit }, lang) {
|
||||
window.localStorage['gc_language'] = lang
|
||||
Vue.prototype.$timeago.setCurrentLocale(lang)
|
||||
commit('SET_LANGUAGE', lang)
|
||||
},
|
||||
setMouseSupport ({ commit }, value) {
|
||||
window.localStorage['gc_mouse'] = value
|
||||
PhoneAPI.setUseMouse(value)
|
||||
commit('SET_MOUSE_SUPPORT', value)
|
||||
},
|
||||
closePhone () {
|
||||
PhoneAPI.closePhone()
|
||||
},
|
||||
resetPhone ({ dispatch, getters }) {
|
||||
dispatch('setZoon', '100%')
|
||||
dispatch('setVolume', 1)
|
||||
dispatch('setBackground', getters.config.background_default)
|
||||
dispatch('setCoque', getters.config.coque_default)
|
||||
dispatch('setSonido', getters.config.sonido_default)
|
||||
dispatch('setLanguage', 'fr_FR')
|
||||
}
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
SET_CONFIG (state, config) {
|
||||
state.config = config
|
||||
},
|
||||
SET_APP_ENABLE (state, {appName, enable}) {
|
||||
const appIndex = state.config.apps.findIndex(app => app.name === appName)
|
||||
if (appIndex !== -1) {
|
||||
Vue.set(state.config.apps[appIndex], 'enabled', enable)
|
||||
}
|
||||
},
|
||||
SET_PHONE_VISIBILITY (state, show) {
|
||||
state.show = show
|
||||
state.tempoHide = false
|
||||
},
|
||||
SET_TEMPO_HIDE (state, hide) {
|
||||
state.tempoHide = hide
|
||||
},
|
||||
SET_MY_PHONE_NUMBER (state, myPhoneNumber) {
|
||||
state.myPhoneNumber = myPhoneNumber
|
||||
},
|
||||
SET_BACKGROUND (state, background) {
|
||||
state.background = background
|
||||
},
|
||||
SET_COQUE (state, coque) {
|
||||
state.coque = coque
|
||||
},
|
||||
SET_SONIDO (state, sonido) {
|
||||
state.sonido = sonido
|
||||
},
|
||||
SET_ZOOM (state, zoom) {
|
||||
state.zoom = zoom
|
||||
},
|
||||
SET_VOLUME (state, volume) {
|
||||
state.volume = volume
|
||||
},
|
||||
SET_LANGUAGE (state, lang) {
|
||||
state.lang = lang
|
||||
},
|
||||
SET_MOUSE_SUPPORT (state, value) {
|
||||
state.mouse = value
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations
|
||||
}
|
||||
116
HTML/gcphone/src/store/modules/tchat.js
Normal file
@@ -0,0 +1,116 @@
|
||||
import PhoneAPI from './../../PhoneAPI'
|
||||
const LOCAL_NAME = 'gc_tchat_channels'
|
||||
|
||||
let TchatAudio = null
|
||||
|
||||
const state = {
|
||||
channels: JSON.parse(localStorage[LOCAL_NAME] || null) || [],
|
||||
currentChannel: null,
|
||||
messagesChannel: []
|
||||
}
|
||||
|
||||
const getters = {
|
||||
tchatChannels: ({ channels }) => channels,
|
||||
tchatCurrentChannel: ({ currentChannel }) => currentChannel,
|
||||
tchatMessages: ({ messagesChannel }) => messagesChannel
|
||||
}
|
||||
|
||||
const actions = {
|
||||
tchatReset ({commit}) {
|
||||
commit('TCHAT_SET_MESSAGES', { messages: [] })
|
||||
commit('TCHAT_SET_CHANNEL', { channel: null })
|
||||
commit('TCHAT_REMOVES_ALL_CHANNELS')
|
||||
},
|
||||
tchatSetChannel ({ state, commit, dispatch }, { channel }) {
|
||||
if (state.currentChannel !== channel) {
|
||||
commit('TCHAT_SET_MESSAGES', { messages: [] })
|
||||
commit('TCHAT_SET_CHANNEL', { channel })
|
||||
dispatch('tchatGetMessagesChannel', { channel })
|
||||
}
|
||||
},
|
||||
tchatAddMessage ({ state, commit, getters }, { message }) {
|
||||
const channel = message.channel
|
||||
if (state.channels.find(e => e.channel === channel) !== undefined) {
|
||||
if (TchatAudio !== null) {
|
||||
TchatAudio.pause()
|
||||
TchatAudio = null
|
||||
}
|
||||
TchatAudio = new Audio('/html/static/sound/tchatNotification.ogg')
|
||||
TchatAudio.volume = getters.volume
|
||||
TchatAudio.play()
|
||||
}
|
||||
commit('TCHAT_ADD_MESSAGES', { message })
|
||||
},
|
||||
tchatAddChannel ({ commit }, { channel }) {
|
||||
commit('TCHAT_ADD_CHANNELS', { channel })
|
||||
},
|
||||
tchatRemoveChannel ({ commit }, { channel }) {
|
||||
commit('TCHAT_REMOVES_CHANNELS', { channel })
|
||||
},
|
||||
tchatGetMessagesChannel ({ commit }, { channel }) {
|
||||
PhoneAPI.tchatGetMessagesChannel(channel)
|
||||
},
|
||||
tchatSendMessage (state, { channel, message }) {
|
||||
PhoneAPI.tchatSendMessage(channel, message)
|
||||
}
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
TCHAT_SET_CHANNEL (state, { channel }) {
|
||||
state.currentChannel = channel
|
||||
},
|
||||
TCHAT_ADD_CHANNELS (state, { channel }) {
|
||||
state.channels.push({
|
||||
channel
|
||||
})
|
||||
localStorage[LOCAL_NAME] = JSON.stringify(state.channels)
|
||||
},
|
||||
TCHAT_REMOVES_CHANNELS (state, { channel }) {
|
||||
state.channels = state.channels.filter(c => {
|
||||
return c.channel !== channel
|
||||
})
|
||||
localStorage[LOCAL_NAME] = JSON.stringify(state.channels)
|
||||
},
|
||||
TCHAT_REMOVES_ALL_CHANNELS (state) {
|
||||
state.channels = []
|
||||
localStorage[LOCAL_NAME] = JSON.stringify(state.channels)
|
||||
},
|
||||
TCHAT_ADD_MESSAGES (state, { message }) {
|
||||
if (message.channel === state.currentChannel) {
|
||||
state.messagesChannel.push(message)
|
||||
}
|
||||
},
|
||||
TCHAT_SET_MESSAGES (state, { messages }) {
|
||||
state.messagesChannel = messages
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
state.currentChannel = 'debug'
|
||||
state.messagesChannel = JSON.parse('[{"channel":"teste","message":"teste","id":6,"time":1528671680000},{"channel":"teste","message":"Hop","id":5,"time":1528671153000}]')
|
||||
for (let i = 0; i < 200; i++) {
|
||||
state.messagesChannel.push(Object.assign({}, state.messagesChannel[0], { id: 100 + i, message: 'mess ' + i }))
|
||||
}
|
||||
state.messagesChannel.push({
|
||||
message: 'Message sur plusieur ligne car il faut bien !!! Ok !',
|
||||
id: 5000,
|
||||
time: new Date().getTime()
|
||||
})
|
||||
state.messagesChannel.push({
|
||||
message: 'Message sur plusieur ligne car il faut bien !!! Ok !',
|
||||
id: 5000,
|
||||
time: new Date().getTime()
|
||||
})
|
||||
state.messagesChannel.push({
|
||||
message: 'Message sur plusieur ligne car il faut bien !!! Ok !',
|
||||
id: 5000,
|
||||
time: new Date(4567845).getTime()
|
||||
})
|
||||
}
|
||||
205
HTML/gcphone/src/store/modules/twitter.js
Normal file
@@ -0,0 +1,205 @@
|
||||
import PhoneAPI from './../../PhoneAPI'
|
||||
import Vue from 'vue'
|
||||
|
||||
const state = {
|
||||
twitterUsername: localStorage['gcphone_twitter_username'],
|
||||
twitterPassword: localStorage['gcphone_twitter_password'],
|
||||
twitterAvatarUrl: localStorage['gcphone_twitter_avatarUrl'],
|
||||
twitterNotification: localStorage['gcphone_twitter_notif'] ? parseInt(localStorage['gcphone_twitter_notif']) : 1,
|
||||
twitterNotificationSound: localStorage['gcphone_twitter_notif_sound'] !== 'false',
|
||||
tweets: [],
|
||||
favoriteTweets: []
|
||||
}
|
||||
|
||||
const getters = {
|
||||
twitterUsername: ({ twitterUsername }) => twitterUsername,
|
||||
twitterPassword: ({ twitterPassword }) => twitterPassword,
|
||||
twitterAvatarUrl: ({ twitterAvatarUrl }) => twitterAvatarUrl,
|
||||
twitterNotification: ({ twitterNotification }) => twitterNotification,
|
||||
twitterNotificationSound: ({ twitterNotificationSound }) => twitterNotificationSound,
|
||||
tweets: ({ tweets }) => tweets,
|
||||
favoriteTweets: ({ favoriteTweets }) => favoriteTweets
|
||||
}
|
||||
|
||||
const actions = {
|
||||
twitterCreateNewAccount (_, {username, password, avatarUrl}) {
|
||||
PhoneAPI.twitter_createAccount(username, password, avatarUrl)
|
||||
},
|
||||
twitterLogin ({ commit }, { username, password }) {
|
||||
PhoneAPI.twitter_login(username, password)
|
||||
},
|
||||
twitterChangePassword ({ state }, newPassword) {
|
||||
PhoneAPI.twitter_changePassword(state.twitterUsername, state.twitterPassword, newPassword)
|
||||
},
|
||||
twitterLogout ({ commit }) {
|
||||
localStorage.removeItem('gcphone_twitter_username')
|
||||
localStorage.removeItem('gcphone_twitter_password')
|
||||
localStorage.removeItem('gcphone_twitter_avatarUrl')
|
||||
commit('UPDATE_ACCOUNT', {
|
||||
username: undefined,
|
||||
password: undefined,
|
||||
avatarUrl: undefined
|
||||
})
|
||||
},
|
||||
twitterSetAvatar ({ state }, { avatarUrl }) {
|
||||
PhoneAPI.twitter_setAvatar(state.twitterUsername, state.twitterPassword, avatarUrl)
|
||||
},
|
||||
twitterPostTweet ({ state, commit }, { message }) {
|
||||
if (/^https?:\/\/.*\.(png|jpg|jpeg|gif)$/.test(message)) {
|
||||
PhoneAPI.twitter_postTweetImg(state.twitterUsername, state.twitterPassword, message)
|
||||
} else {
|
||||
PhoneAPI.twitter_postTweet(state.twitterUsername, state.twitterPassword, PhoneAPI.convertEmoji(message))
|
||||
}
|
||||
},
|
||||
twitterToogleLike ({ state }, { tweetId }) {
|
||||
PhoneAPI.twitter_toggleLikeTweet(state.twitterUsername, state.twitterPassword, tweetId)
|
||||
},
|
||||
setAccount ({ commit }, data) {
|
||||
localStorage['gcphone_twitter_username'] = data.username
|
||||
localStorage['gcphone_twitter_password'] = data.password
|
||||
localStorage['gcphone_twitter_avatarUrl'] = data.avatarUrl
|
||||
commit('UPDATE_ACCOUNT', data)
|
||||
},
|
||||
addTweet ({ commit, state }, tweet) {
|
||||
let notif = state.twitterNotification === 2
|
||||
if (state.twitterNotification === 1) {
|
||||
notif = tweet.message && tweet.message.toLowerCase().indexOf(state.twitterUsername.toLowerCase()) !== -1
|
||||
}
|
||||
if (notif === true) {
|
||||
Vue.notify({
|
||||
message: tweet.message,
|
||||
title: tweet.author + ' :',
|
||||
icon: 'twitter',
|
||||
sound: state.twitterNotificationSound ? 'Twitter_Sound_Effect.ogg' : undefined
|
||||
})
|
||||
}
|
||||
commit('ADD_TWEET', { tweet })
|
||||
},
|
||||
fetchTweets ({ state }) {
|
||||
PhoneAPI.twitter_getTweets(state.twitterUsername, state.twitterPassword)
|
||||
},
|
||||
fetchFavoriteTweets ({ state }) {
|
||||
PhoneAPI.twitter_getFavoriteTweets(state.twitterUsername, state.twitterPassword)
|
||||
},
|
||||
setTwitterNotification ({ commit }, value) {
|
||||
localStorage['gcphone_twitter_notif'] = value
|
||||
commit('SET_TWITTER_NOTIFICATION', { notification: value })
|
||||
},
|
||||
setTwitterNotificationSound ({ commit }, value) {
|
||||
localStorage['gcphone_twitter_notif_sound'] = value
|
||||
commit('SET_TWITTER_NOTIFICATION_SOUND', { notificationSound: value })
|
||||
}
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
SET_TWITTER_NOTIFICATION (state, { notification }) {
|
||||
state.twitterNotification = notification
|
||||
},
|
||||
SET_TWITTER_NOTIFICATION_SOUND (state, { notificationSound }) {
|
||||
state.twitterNotificationSound = notificationSound
|
||||
},
|
||||
UPDATE_ACCOUNT (state, { username, password, avatarUrl }) {
|
||||
state.twitterUsername = username
|
||||
state.twitterPassword = password
|
||||
state.twitterAvatarUrl = avatarUrl
|
||||
},
|
||||
SET_TWEETS (state, { tweets }) {
|
||||
state.tweets = tweets
|
||||
},
|
||||
SET_FAVORITE_TWEETS (state, { tweets }) {
|
||||
state.favoriteTweets = tweets
|
||||
},
|
||||
ADD_TWEET (state, { tweet }) {
|
||||
state.tweets = [tweet, ...state.tweets]
|
||||
},
|
||||
UPDATE_TWEET_LIKE (state, { tweetId, likes }) {
|
||||
const tweetIndex = state.tweets.findIndex(t => t.id === tweetId)
|
||||
if (tweetIndex !== -1) {
|
||||
state.tweets[tweetIndex].likes = likes
|
||||
}
|
||||
const tweetIndexFav = state.favoriteTweets.findIndex(t => t.id === tweetId)
|
||||
if (tweetIndexFav !== -1) {
|
||||
state.favoriteTweets[tweetIndexFav].likes = likes
|
||||
}
|
||||
},
|
||||
UPDATE_TWEET_ISLIKE (state, { tweetId, isLikes }) {
|
||||
const tweetIndex = state.tweets.findIndex(t => t.id === tweetId)
|
||||
if (tweetIndex !== -1) {
|
||||
Vue.set(state.tweets[tweetIndex], 'isLikes', isLikes)
|
||||
}
|
||||
const tweetIndexFav = state.favoriteTweets.findIndex(t => t.id === tweetId)
|
||||
if (tweetIndexFav !== -1) {
|
||||
Vue.set(state.favoriteTweets[tweetIndexFav], 'isLikes', isLikes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
state.favoriteTweets = [{
|
||||
id: 1,
|
||||
message: 'https://pbs.twimg.com/profile_images/702982240184107008/tUKxvkcs_400x400.jpg',
|
||||
author: 'Gannon',
|
||||
time: new Date(),
|
||||
likes: 3,
|
||||
isLikes: 60
|
||||
}, {
|
||||
id: 2,
|
||||
message: 'Borderlands 3 arrives on Xbox One, PS4, and PC on September 13, 2019! Tune in to the Gameplay Reveal Event on May 1st, where we’ll debut the first hands-on looks! Pre-order now to get the Gold Weapon Skins Pack! ➜ https://borderlands.com ',
|
||||
author: 'Gearbox Official',
|
||||
authorIcon: 'https://pbs.twimg.com/profile_images/702982240184107008/tUKxvkcs_400x400.jpg',
|
||||
time: new Date(),
|
||||
likes: 65
|
||||
}, {
|
||||
id: 3,
|
||||
message: '',
|
||||
img: 'https://cdn.discordapp.com/attachments/563443658192322576/563473765569396746/samurai-background-hd-1920x1200-45462.jpg',
|
||||
author: 'Gannon',
|
||||
time: new Date()
|
||||
}, {
|
||||
id: 4,
|
||||
message: 'Super Message de la mort.',
|
||||
author: 'Gannon',
|
||||
authorIcon: 'https://pbs.twimg.com/profile_images/986085090684960768/AcD9lOLw_bigger.jpg',
|
||||
likes: 0,
|
||||
time: new Date()
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
message: 'Super Message de la mort.',
|
||||
author: 'Gannon',
|
||||
authorIcon: 'https://pbs.twimg.com/profile_images/986085090684960768/AcD9lOLw_bigger.jpg',
|
||||
likes: 0,
|
||||
time: new Date()
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
message: 'Super Message de la mort.',
|
||||
author: 'Gannon',
|
||||
authorIcon: 'https://pbs.twimg.com/profile_images/986085090684960768/AcD9lOLw_bigger.jpg',
|
||||
likes: 0,
|
||||
time: new Date()
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
message: 'Super Message de la mort.',
|
||||
author: 'Gannon',
|
||||
authorIcon: 'https://pbs.twimg.com/profile_images/986085090684960768/AcD9lOLw_bigger.jpg',
|
||||
likes: 0,
|
||||
time: new Date()
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
message: 'Super Message de la mort.',
|
||||
author: 'Gannon',
|
||||
authorIcon: 'https://pbs.twimg.com/profile_images/986085090684960768/AcD9lOLw_bigger.jpg',
|
||||
likes: 0,
|
||||
time: new Date()
|
||||
}]
|
||||
}
|
||||