Files
spot/src/components/projectPost.vue
2026-01-16 21:46:51 +01:00

164 lines
5.9 KiB
Vue

<script>
import spotIcon from './spotIcon.vue';
import spotButton from './spotButton.vue';
import projectMediaLink from './projectMediaLink.vue';
import projectMapLink from './projectMapLink.vue';
import projectRelTime from './projectRelTime.vue';
import { LngLat } from 'maplibre-gl';
import autosize from 'autosize';
export default {
components: {
spotIcon,
spotButton,
projectMediaLink,
projectMapLink,
projectRelTime
},
props: {
options: Object
},
data() {
return {
mouseOverHeader: false,
absTime: this.options.formatted_time,
absTimeLocal: this.options.formatted_time_local,
timeDiff: (this.options.formatted_time && this.options.formatted_time_local != this.options.formatted_time),
anchorVisible: ['message', 'media', 'post'].includes(this.options.type),
anchorTitle: this.spot.lang('copy_to_clipboard'),
anchorIcon: 'link'
};
},
computed: {
postClass() {
let sHeaderLess = this.options.headerless?' headerless':'';
return 'post-item '+this.options.type+sHeaderLess;
},
postId() {
return this.options.id?(this.options.type+'-'+this.options.id):'';
},
subType() {
return this.options.subtype || this.options.type;
},
displayedId() {
return this.options.displayed_id?(this.spot.lang('counter', this.options.displayed_id)):'';
},
hash() {
let asHash = this.spot.getHash();
return '#'+[asHash.page, asHash.items[0], this.options.type, this.options.id].join(this.spot.consts.hash_sep);
},
modeHisto() {
return (this.project.currProject.mode==this.spot.consts.modes.histo);
},
relTime() {
return this.modeHisto?(this.options.formatted_time || '').substr(0, 10):this.options.relative_time;
},
},
inject: ['spot', 'project', 'user', 'map', 'project'],
methods: {
copyAnchor() {
copyTextToClipboard(this.spot.consts.server+this.spot.hash());
this.anchorTitle = this.spot.lang('link_copied');
this.anchorIcon = 'copied';
setTimeout(()=>{ //TODO animation
this.anchorTitle = this.spot.lang('copy_to_clipboard');
this.anchorIcon = 'link';
}, 5000);
},
panMapToMessage() {
if(this.spot.isMobile()) this.project.toggleFeedPanel(false, 'panToInstant');
this.map.panTo(
new LngLat(this.options.longitude, this.options.latitude),
15,
() => {this.map.openMarkerPopup(this.options.id_message);}
);
},
openMarkerPopup() {
//TODO
/*
let oMarker = this.spot.tmp(['markers', $(oEvent.currentTarget).data('id')]);
if(this.spot.tmp('map') && this.spot.tmp('map').getBounds().contains(oMarker.getLatLng()) && !oMarker.isPopupOpen()) oMarker.openPopup();
*/
},
closeMarkerPopup() {
//TODO
/*
let $This = $(oEvent.currentTarget);
let oMarker = this.spot.tmp(['markers', $This.data('id')]);
if(oMarker && oMarker.isPopupOpen() && !$This.data('clicked')) oMarker.closePopup();
$This.data('clicked', false);
*/
}
},
mounted() {
//Auto-adjust text area height
if(this.options.type == 'poster') autosize(this.$refs.post);
}
}
</script>
<template>
<div :class="postClass" :id="postId">
<div class="header">
<div class="index">
<spotIcon :icon="subType" :text="displayedId" />
<a v-if="anchorVisible" class="link desktop" @click="copyAnchor" ref="anchor" :href="hash" :title="anchorTitle">
<spotIcon :icon="anchorIcon" />
</a>
</div>
<div class="time" @mouseleave="mouseOverHeader = false" @mouseover="mouseOverHeader = true" :title="timeDiff?spot.lang('local_time', absTimeLocal):''">
<Transition name="fade" mode="out-in">
<span v-if="mouseOverHeader">{{ timeDiff?spot.lang('your_time', absTime):absTime }}</span>
<span v-else>{{ relTime }}</span>
</Transition>
</div>
</div>
<div class="body">
<div v-if="options.type == 'message'" class="body-box" @mouseenter="openMarkerPopup" @mouseleave="closeMarkerPopup">
<p>
<spotIcon :icon="'coords'" :classes="'push'" />
<projectMapLink :options="options" />
</p>
<p v-if="timeDiff">
<projectRelTime :icon="'time'" :iconClasses="'push'" :localTime="absTimeLocal" :siteTime="options.formatted_time" :offset="options.day_offset" />
</p>
<a class="drill" @click.prevent="panMapToMessage">
<span v-if="options.weather_icon && options.weather_icon!='unknown'" class="weather clickable" :title="spot.lang(options.weather_cond)">
<spotIcon :icon="options.weather_icon" />
<span class="temperature">{{ options.weather_temp+'°C' }}</span>
</span>
<img class="staticmap clickable" :title="spot.lang('click_zoom')" :src="options.static_img_url" />
<span class="drill-icon fa-stack clickable">
<spotIcon :icon="'message'" :classes="'fa-stack-2x clickable'" />
<spotIcon :icon="'message-in'" :classes="'fa-stack-1x fa-rotate-270'" />
</span>
</a>
</div>
<div v-else-if="options.type == 'media'" class="body-box">
<projectMediaLink :options="options" :type="'post'" />
</div>
<div v-else-if="options.type == 'post'">
<p class="message">{{ options.content }}</p>
<p class="signature">
<img v-if="options.gravatar" :src="'data:image/png;base64, '+options.gravatar" width="24" height="24" alt="--" />
<span v-else>-- </span>
<span>{{ options.formatted_name }}</span>
</p>
</div>
<p v-else-if="options.type == 'poster'" class="message">
<textarea ref="post" name="post" :placeholder="spot.lang('post_message')" class="autoExpand" rows="1" v-model="$parent.post"></textarea>
<input type="text" name="name" :placeholder="spot.lang('post_name')" v-model="user.name" />
<spotButton name="submit" :aria-label="spot.lang('send')" :title="spot.lang('send')" :icon="'send'" />
</p>
<div v-else-if="options.type == 'archived'">
<p><spotIcon :icon="'success'" /></p>
<p>{{ spot.lang('mode_histo') }}</p>
</div>
<div v-else-if="options.type == 'loading'">
<p class="flicker"><spotIcon :icon="'post'" /></p>
</div>
</div>
</div>
</template>