commit
034cfdb610
|
@ -6,6 +6,10 @@ let analyticsEnabled
|
|||
const posthogConfigured = process.env.POSTHOG_TOKEN && process.env.POSTHOG_URL
|
||||
const sentryConfigured = process.env.SENTRY_DSN
|
||||
|
||||
const FEEDBACK_SUBMITTED_KEY = "budibase:feedback_submitted"
|
||||
const APP_FIRST_STARTED_KEY = "budibase:first_run"
|
||||
const feedbackHours = 12
|
||||
|
||||
async function activate() {
|
||||
if (analyticsEnabled === undefined) {
|
||||
// only the server knows the true NODE_ENV
|
||||
|
@ -62,10 +66,50 @@ function captureEvent(eventName, props = {}) {
|
|||
posthog.capture(eventName, props)
|
||||
}
|
||||
|
||||
if (!localStorage.getItem(APP_FIRST_STARTED_KEY)) {
|
||||
localStorage.setItem(APP_FIRST_STARTED_KEY, Date.now())
|
||||
}
|
||||
|
||||
const isFeedbackTimeElapsed = sinceDateStr => {
|
||||
const sinceDate = parseFloat(sinceDateStr)
|
||||
const feedbackMilliseconds = feedbackHours * 60 * 60 * 1000
|
||||
return Date.now() > sinceDate + feedbackMilliseconds
|
||||
}
|
||||
function submitFeedback(values) {
|
||||
if (!analyticsEnabled || !process.env.POSTHOG_TOKEN) return
|
||||
localStorage.setItem(FEEDBACK_SUBMITTED_KEY, Date.now())
|
||||
|
||||
const prefixedValues = Object.entries(values).reduce((obj, [key, value]) => {
|
||||
obj[`feedback_${key}`] = value
|
||||
return obj
|
||||
}, {})
|
||||
|
||||
posthog.capture("Feedback Submitted", prefixedValues)
|
||||
}
|
||||
|
||||
function requestFeedbackOnDeploy() {
|
||||
if (!analyticsEnabled || !process.env.POSTHOG_TOKEN) return false
|
||||
const lastSubmittedStr = localStorage.getItem(FEEDBACK_SUBMITTED_KEY)
|
||||
if (!lastSubmittedStr) return true
|
||||
return isFeedbackTimeElapsed(lastSubmittedStr)
|
||||
}
|
||||
|
||||
function highlightFeedbackIcon() {
|
||||
if (!analyticsEnabled || !process.env.POSTHOG_TOKEN) return false
|
||||
const lastSubmittedStr = localStorage.getItem(FEEDBACK_SUBMITTED_KEY)
|
||||
if (lastSubmittedStr) return isFeedbackTimeElapsed(lastSubmittedStr)
|
||||
const firstRunStr = localStorage.getItem(APP_FIRST_STARTED_KEY)
|
||||
if (!firstRunStr) return false
|
||||
return isFeedbackTimeElapsed(firstRunStr)
|
||||
}
|
||||
|
||||
export default {
|
||||
activate,
|
||||
identify,
|
||||
identifyByApiKey,
|
||||
captureException,
|
||||
captureEvent,
|
||||
requestFeedbackOnDeploy,
|
||||
submitFeedback,
|
||||
highlightFeedbackIcon,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
height="24">
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M6.455 19L2 22.5V4a1 1 0 0 1 1-1h18a1 1 0 0 1 1 1v14a1 1 0 0 1-1
|
||||
1H6.455zM11 13v2h2v-2h-2zm0-6v5h2V7h-2z" />
|
||||
</svg>
|
After Width: | Height: | Size: 292 B |
|
@ -33,3 +33,4 @@ export { default as InfoIcon } from "./Info.svelte"
|
|||
export { default as CloseIcon } from "./Close.svelte"
|
||||
export { default as MoreIcon } from "./More.svelte"
|
||||
export { default as CloseCircleIcon } from "./CloseCircle.svelte"
|
||||
export { default as FeedbackIcon } from "./Feedback.svelte"
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
<script>
|
||||
import analytics from "analytics"
|
||||
import { createEventDispatcher } from "svelte"
|
||||
import { store } from "builderStore"
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const feedbackUrl = "https://feedback.budibase.com"
|
||||
|
||||
let iframe
|
||||
|
||||
// the app @ feedback.budibase.com expects to be loaded
|
||||
// in an iframe, and posts messages back.
|
||||
// this means that we can submit using the Builder's posthog setup
|
||||
window.addEventListener(
|
||||
"message",
|
||||
function(ev) {
|
||||
if (ev.origin !== feedbackUrl) return
|
||||
|
||||
if (ev.data.type === "loaded") {
|
||||
iframe.setAttribute(
|
||||
"style",
|
||||
`height:${ev.data.height}px; width:${ev.data.width}px`
|
||||
)
|
||||
} else if (ev.data.type === "submitted") {
|
||||
analytics.submitFeedback(ev.data.data)
|
||||
$store.highlightFeedbackIcon = false
|
||||
dispatch("finished")
|
||||
}
|
||||
},
|
||||
false
|
||||
)
|
||||
</script>
|
||||
|
||||
<iframe src={feedbackUrl} title="feedback" bind:this={iframe}>
|
||||
<html lang="html">
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div>Loading...</div>
|
||||
</body>
|
||||
</html>
|
||||
</iframe>
|
||||
|
||||
<style>
|
||||
iframe {
|
||||
border-style: none;
|
||||
height: auto;
|
||||
overflow-y: hidden;
|
||||
overflow-x: hidden;
|
||||
min-width: 500px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,56 @@
|
|||
<script>
|
||||
import { FeedbackIcon } from "components/common/Icons/"
|
||||
import { Popover } from "@budibase/bbui"
|
||||
import { store } from "builderStore"
|
||||
|
||||
import FeedbackIframe from "./FeedbackIframe.svelte"
|
||||
import analytics from "analytics"
|
||||
|
||||
const FIVE_MINUTES = 300000
|
||||
|
||||
let iconContainer
|
||||
let popover
|
||||
|
||||
setInterval(() => {
|
||||
$store.highlightFeedbackIcon = analytics.highlightFeedbackIcon()
|
||||
}, FIVE_MINUTES)
|
||||
</script>
|
||||
|
||||
<span
|
||||
class="container"
|
||||
bind:this={iconContainer}
|
||||
on:click={popover.show}
|
||||
class:highlight={$store.highlightFeedbackIcon}>
|
||||
<FeedbackIcon />
|
||||
</span>
|
||||
<Popover bind:this={popover} anchor={iconContainer} align="right">
|
||||
<FeedbackIframe on:finished={popover.hide} />
|
||||
</Popover>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
cursor: pointer;
|
||||
color: var(--grey-7);
|
||||
margin: 0 20px 0 0;
|
||||
font-weight: 500;
|
||||
font-size: 1rem;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.container:hover {
|
||||
color: var(--ink);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: var(--blue);
|
||||
}
|
||||
|
||||
.highlight > :global(svg) {
|
||||
filter: drop-shadow(0 0 20px var(--blue));
|
||||
}
|
||||
</style>
|
|
@ -2,6 +2,7 @@
|
|||
import { store, automationStore, backendUiStore } from "builderStore"
|
||||
import { Button } from "@budibase/bbui"
|
||||
import SettingsLink from "components/settings/Link.svelte"
|
||||
import FeedbackNavLink from "components/userInterface/Feedback/FeedbackNavLink.svelte"
|
||||
import { get } from "builderStore/api"
|
||||
import { isActive, goto, layout } from "@sveltech/routify"
|
||||
import { PreviewIcon } from "components/common/Icons/"
|
||||
|
@ -65,6 +66,7 @@
|
|||
{/each}
|
||||
</div>
|
||||
<div class="toprightnav">
|
||||
<FeedbackNavLink />
|
||||
<SettingsLink />
|
||||
<span
|
||||
class:active={false}
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
<script>
|
||||
import { onMount } from "svelte"
|
||||
import { Button, Spacer } from "@budibase/bbui"
|
||||
import { Button, Spacer, Modal } from "@budibase/bbui"
|
||||
import { store } from "builderStore"
|
||||
import { notifier } from "builderStore/store/notifications"
|
||||
import api from "builderStore/api"
|
||||
import Spinner from "components/common/Spinner.svelte"
|
||||
import DeploymentHistory from "components/deploy/DeploymentHistory.svelte"
|
||||
import analytics from "analytics"
|
||||
import FeedbackIframe from "components/userInterface/Feedback/FeedbackIframe.svelte"
|
||||
|
||||
let loading = false
|
||||
let deployments = []
|
||||
let poll
|
||||
let feedbackModal
|
||||
|
||||
$: appId = $store.appId
|
||||
|
||||
|
@ -31,6 +33,10 @@
|
|||
analytics.captureEvent("Deployed App", {
|
||||
appId,
|
||||
})
|
||||
|
||||
if (analytics.requestFeedbackOnDeploy()) {
|
||||
feedbackModal.show()
|
||||
}
|
||||
} catch (err) {
|
||||
analytics.captureEvent("Deploy App Failed", {
|
||||
appId,
|
||||
|
@ -57,6 +63,9 @@
|
|||
src="/_builder/assets/deploy-rocket.jpg"
|
||||
alt="Rocket flying through sky" />
|
||||
</section>
|
||||
<Modal bind:this={feedbackModal}>
|
||||
<FeedbackIframe on:finished={() => feedbackModal.hide()} />
|
||||
</Modal>
|
||||
<DeploymentHistory {appId} />
|
||||
|
||||
<style>
|
||||
|
|
Loading…
Reference in New Issue