Open psi-4ward opened 4 years ago
Hi @psi-4ward, I think I'd rather refactor client.js such that it could be installed as a framework agnostic npm package and bundled in any type of app:
yarn install utterances
then something like:
import { styles, createElement } from 'utterances';
// vanilla usage:
document.head.insertAdjacentHTML('afterbegin', `<style>${styles}</style>`);
const utterances = createElement(... options...);
document.body.appendChild(utterances);
Would this make it easier to integrate with Vue?
@jdanyow yeah, that would help a lot. Although Vue usage would probably prefer to use bundler CSS imports (i.e. import utterances/theme.css
).
@psi-4ward in the meantime, I threw this together for use with Gridsome (but you could easily use anything else easily):
<template>
<div class="comments" :style="{ height: `${height}px` }">
<iframe ref="comments" scrolling="no" :src="url" />
</div>
</template>
<script lang="ts">
import { Component, Prop, Ref, Vue, Watch } from 'vue-property-decorator';
interface ResizeMessage {
type: 'resize';
height: number;
}
@Component
export default class GithubComments extends Vue {
@Prop({ type: String, required: true }) title!: string;
@Ref() readonly comments!: HTMLIFrameElement;
height = 0;
url = '';
loaded = false;
get theme() {
return this.$theme.dark ? 'dark-blue' : 'github-light';
}
created() {
const parts: Record<string, string> = {
'issue-term': 'title',
url: this.$route.path,
title: this.title,
repo: 'lights0123/lights0123.github.io',
theme: this.theme,
// @ts-ignore
origin: process.isClient ? window.origin : this.$static.metadata.siteUrl,
};
this.url = 'https://utteranc.es/utterances.html?' + Object.keys(parts).map(name => `${encodeURIComponent(name)}=${encodeURIComponent(parts[name])}`).join('&');
}
@Watch('loaded')
@Watch('theme')
sendTheme() {
this.comments.contentWindow?.postMessage({
type: 'set-theme',
theme: this.theme,
}, 'https://utteranc.es');
}
message(event: MessageEvent) {
if (event.origin !== 'https://utteranc.es') return;
if (!this.loaded) this.loaded = true;
const data = event.data as Partial<ResizeMessage>;
if (data && data.type === 'resize' && data.height) {
this.height = data.height;
}
}
mounted() {
window.addEventListener('message', this.message);
}
beforeDestroy() {
window.removeEventListener('message', this.message);
}
}
</script>
<style lang="scss">
.comments {
position: relative;
box-sizing: border-box;
width: 100%;
margin-left: auto;
margin-right: auto;
iframe {
position: absolute;
left: 0;
right: 0;
width: 1px;
min-width: 100%;
max-width: 100%;
height: 100%;
border: 0;
}
}
</style>
<static-query>
query {
metadata {
siteUrl
}
}
</static-query>
@jdanyow yeah, that would help a lot. Although Vue usage would probably prefer to use bundler CSS imports (i.e.
import utterances/theme.css
).@psi-4ward in the meantime, I threw this together for use with Gridsome (but you could easily use anything else easily):
<template> <div class="comments" :style="{ height: `${height}px` }"> ...
This one won't work since it does not come with session support. Consider following version.
The file '../utils/deparam' comes from here: https://github.com/utterance/utterances/blob/master/src/deparam.ts
Also refer to the official one: https://github.com/utterance/utterances/blob/master/src/client.ts
<template>
<iframe ref="comments" scrolling="no" :src="data.url" :height="data.height" />
</template>
<script lang="ts">
import { defineComponent, onMounted, reactive, watch } from '@vue/composition-api'
import { useBeforeMount } from '../utils/hooks'
import { param, deparam } from '../utils/deparam'
interface ResizeMessage {
type: 'resize';
height: number;
}
export default defineComponent({
name: 'UtterancesVue',
props: {
title: {
type: String,
required: true,
},
redirectUrl: {
type: String,
required: true,
},
repo: {
type: String,
required: true
},
theme: {
type: String,
required: true
},
label: String,
issueNumber: Number
},
setup (props, { root, refs }) {
const data = reactive({
height: 0,
url: '',
loaded: false
})
onMounted(() => {
// get session
const session = deparam(location.search.substr(1))
if (session?.utterances) {
localStorage.setItem('utterances-session', session.utterances)
delete session.utterances
let search = param(session)
if (search.length) {
search = '?' + search
}
history.replaceState(undefined, document.title, location.pathname + search + location.hash)
}
// setup
const params = {
'issue-term': 'title',
// @ts-ignore
url: props.redirectUrl,
title: props.title,
label: props.label,
repo: props.repo,
theme: props.theme,
pathname: location.pathname,
// @ts-ignore
origin: process.isClient ? window.origin : root.$static.metadata.siteUrl,
session: session?.utterances || localStorage.getItem('utterances-session') || '',
}
data.url = 'https://utteranc.es/utterances.html?' + Object.keys(params)
// @ts-ignore
.map(name => `${encodeURIComponent(name)}=${encodeURIComponent(params[name])}`).join('&')
})
// @ts-ignore
watch(() => props.theme, () => refs.contentWindow?.postMessage({
type: 'set-theme',
theme: props.theme,
}, 'https://utteranc.es'))
const messageHandler = (event: MessageEvent) => {
if (event.origin !== 'https://utteranc.es') return
if (!data.loaded) data.loaded = true
const res = event.data as Partial<ResizeMessage>
if (res && res.type === 'resize' && res.height) {
data.height = res.height
}
}
useBeforeMount(() => {
window.addEventListener('message', messageHandler)
return () => window.removeEventListener('message', messageHandler)
})
return {
data
}
}
})
</script>
<style scoped>
iframe {
width: 100%;
min-width: 100%;
max-width: 100%;
border: 0;
outline: none;
}
</style>
<static-query>
query {
metadata {
siteUrl
}
}
</static-query>
Hi mates, I looked at client.js but I think to re-create a
<script>
-Tag in an SPA env is not the preferred way.So I came up and drafted an Utterances Vue Component which actually (should) do the same as the client.js but in the Vue-way. More precisely, it generates and updates the
<iframe>
.Pls have a look at https://github.com/AskSinPP/asksinpp.de/blob/master/.vuepress/theme/components/UtterancesComments.vue
If you find any frictions, please let me know. Perhaps we could transfer this Component into your Repo or create an official one in your org. Or we develop an entire Vuepress-Plugin for utterances ;)
PS: the project name is not very google-friendly :D