Skip to content
This repository was archived by the owner on Feb 26, 2025. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions src/lib/components/AdInternals.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<script lang="ts">
import { envBool } from "$lib/utils/env-helper";
import { sha3_256 } from "@noble/hashes/sha3";
import { bytesToHex } from "@noble/hashes/utils";
import { onDestroy, onMount } from "svelte";
import PrivacyIcon from "./icons/PrivacyIcon.svelte";
import log from "$lib/utils/logger";

export type UnitType = "bottom_rail" | "sky_atf" | "med_rect_atf" | "right_rail";

interface Props {
name: string;
type: UnitType;
}

const unitSize: { [type in UnitType]: { width: number; height: number } } = {
bottom_rail: { width: 320, height: 50 },
sky_atf: { width: 160, height: 600 },
med_rect_atf: { width: 300, height: 250 },
right_rail: { width: 120, height: 600 },
};

const { name, type }: Props = $props();

const displayPlaceholders = envBool("DB_DISPLAY_AD_PLACEHOLDERS");

const selectorName = $derived.by(() => {
const base = name + ":" + type;
const hash = bytesToHex(sha3_256(base));
return `dbu_${hash}`;
});

const size = $derived(unitSize[type]);

onMount(async () => {
if (displayPlaceholders) {
return;
}

try {
await window.ramp.addUnits([
{
selectorId: selectorName,
type,
},
]);
window.ramp.displayUnits();
log.debug(`ramp: initialized unit ${name} (${type})`);
} catch (error) {
log.error("ramp: could not add unit", { error });
window.ramp.displayUnits();
}
});

onDestroy(() => {
if (displayPlaceholders) {
return;
}

const elem = document.getElementById(selectorName);

if (!elem) {
return;
}

window.ramp.destroyUnits(selectorName).then(() => {
window.ramp.processPage(window.location.pathname);
});
});
</script>

{#if displayPlaceholders}
<div
class="flex justify-center items-center border-dashed border-4 border-primary m-2 bg-base-100"
style={`min-width: ${size.width}px !important; min-height: ${size.height}px !important;`}
>
<div class="text-primary scale-[2.0]">
<PrivacyIcon />
</div>
</div>
{:else}
<div id={selectorName}></div>
{/if}
21 changes: 21 additions & 0 deletions src/lib/components/AdSpace.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script lang="ts">
import { pwInitialized } from "$lib/state.svelte";
import { envBool } from "$lib/utils/env-helper";
import AdInternals, { type UnitType } from "./AdInternals.svelte";

interface Props {
name: string;
type: UnitType;
wrapperClasses?: string;
}

const { name, type, wrapperClasses }: Props = $props();

const adsEnabled = envBool("DB_ENABLE_ADS");
</script>

{#if adsEnabled && $pwInitialized}
<div class={"ads " + (wrapperClasses ?? "")}>
<AdInternals {name} {type} />
</div>
{/if}
3 changes: 1 addition & 2 deletions src/lib/components/DrawerMenu.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,9 @@ const version = patch?.version;
</a>
</li>
<li>
<a href="/privacy" class="disabled">
<a href="/privacy">
<PrivacyIcon />
{ $t("menu-privacy") }
<span class="badge badge-ghost">{$t("term-soon")}</span>
</a>
</li>

Expand Down
105 changes: 105 additions & 0 deletions src/lib/components/TrackingRampSetup.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<script lang="ts">
import { afterNavigate } from "$app/navigation";
import { pwInitialized } from "$lib/state.svelte";
import { env, envBool } from "$lib/utils/env-helper";
import log from "$lib/utils/logger";
import { onMount } from "svelte";

let adsEnabledForUser = $state(true); // TODO: default this to false, check with "backend" and then load stuff

const adsEnabled = envBool("DB_ENABLE_ADS");
const displayPlaceholders = envBool("DB_DISPLAY_AD_PLACEHOLDERS");

const publisherId = env("DB_PW_PUBLISHER_ID");
const websiteId = env("DB_PW_WEBSITE_ID");
const ga4MeasurementId = env("DB_GA4_MEASUREMENT_ID");

const init = async () => {
window.gtag("js", new Date());
window.gtag("config", ga4MeasurementId);

log.debug("ga4 initialized");

// feature flag hasn't been enabled
if (!adsEnabled) {
log.debug("ads: disabled");
return;
}

// user doesnt have ads so stop here, might want to check here :)
if (!adsEnabledForUser) {
log.debug("ads: disabled for user");
return;
}

// if we are displaying placeholders instead, just stop here
if (displayPlaceholders) {
log.debug("ads: display placeholder");
return;
}

// not properly set up?
if (!publisherId || !websiteId) {
log.debug("ads: pw data not setup correctly");
return;
}

// ramp has already been initialized
if (window.ramp) {
return;
}

window.ramp = window.ramp ?? {};
window.ramp.que = window.ramp.que ?? [];
window.ramp.passiveMode = true;

const gtagFunc = (...args: unknown[]) => window.dataLayer.push(args);

window._pwGA4PageviewId = Date.now().toString();
window.dataLayer = window.dataLayer || [];
window.gtag = window.gtag || gtagFunc;
window.gtag("js", new Date());
window.gtag("config", ga4MeasurementId, { send_page_view: false });
window.gtag("event", "ramp_js", {
pageview_id: window._pwGA4PageviewId,
send_to: ga4MeasurementId,
});

window.ramp.que.push(() => {
log.debug("playwire has been setup");
pwInitialized.set(true);
});

const rampScript = document.createElement("script");
rampScript.src = `https://cdn.intergient.com/${publisherId}/${websiteId}/ramp.js`;
rampScript.async = true;
document.body.appendChild(rampScript);
};

afterNavigate(() => {
if (!$pwInitialized) {
return;
}

window.ramp.processPage(window.location.pathname);
});

onMount(() => {
// show placeholders even if ga4 is not enabled
if (adsEnabled && displayPlaceholders) {
pwInitialized.set(true);
}
});
</script>

<svelte:head>
{#if ga4MeasurementId}
<script>
window.dataLayer = window.dataLayer || [];
window.gtag = window.gtag || (function () {
window.dataLayer.push(arguments);
});
</script>
<script async src={`https://www.googletagmanager.com/gtag/js?id=${ga4MeasurementId}`} onload={init}></script>
{/if}
</svelte:head>
2 changes: 2 additions & 0 deletions src/lib/state.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,5 @@ export let language = storagable<Language>("lang", determineBrowserLanguage());
export let configViewWeaponAbilities = storagable<boolean>("config.builder.view-weapon-abilities", true);
export let configViewWeaponTalents = storagable<boolean>("config.builder.view-weapon-talents", true);
export let showLanguageWarning = storagable<boolean>("config.app.show-language-warning", true);

export let pwInitialized = writable(false);
11 changes: 11 additions & 0 deletions src/lib/utils/env-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { browser } from "$app/environment";

export const env = (name: string) => {
if (!browser) {
return null;
}

return import.meta.env[name];
};

export const envBool = (name: string) => env(name) === "true";
147 changes: 147 additions & 0 deletions src/lib/utils/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { browser } from "$app/environment";

export enum LogLevel {
Trace,
Debug,
Info,
Warn,
Error,
}

const MAX_LOCAL_STORAGE_LOGS = 500;
const LOCAL_STORAGE_KEY = "db:logs";
const isDevMode = browser ? localStorage.getItem("devmode") === "true" : false;

export class Logger {
private logLevel: LogLevel = isDevMode ? LogLevel.Debug : LogLevel.Info;

private print(level: LogLevel, message: string, data: object = {}) {
const logLevelStr = Logger.prefixByLevel(level);
const logFunc = Logger.logFunctionByLevel(level);

const logMessage = `[${logLevelStr}] ${message}`;

Logger.storeLog(level, logMessage, data);

if (this.logLevel > level) {
return;
}

if (Object.keys(data).length > 0) {
logFunc(logMessage, data);
} else {
logFunc(logMessage);
}
}

public setLogLevel(level: LogLevel) {
this.logLevel = level;
}

public debug(message: string, data: object = {}) {
this.print(LogLevel.Debug, message, data);
}

public info(message: string, data: object = {}) {
this.print(LogLevel.Info, message, data);
}

public warn(message: string, data: object = {}) {
this.print(LogLevel.Warn, message, data);
}

public error(message: string, data: object = {}) {
this.print(LogLevel.Error, message, data);
}

public timer(label: string) {
if (this.logLevel > LogLevel.Debug) {
return;
}

/*eslint-disable*/
console.time(label);
/*eslint-enable*/
}

public timerEnd(label: string) {
if (this.logLevel > LogLevel.Debug) {
return;
}

/*eslint-disable*/
console.timeEnd(label);
/*eslint-enable*/
}

private static logFunctionByLevel(level: LogLevel): (...data: unknown[]) => void {
/*eslint-disable*/
// we do not allow usage of console commands in the project except for this function
switch (level) {
case LogLevel.Trace:
return (...data: unknown[]) => {
console.groupCollapsed(...data);
console.trace();
console.groupEnd();
};
case LogLevel.Debug:
case LogLevel.Info:
return console.info;
case LogLevel.Warn:
return console.warn;
case LogLevel.Error:
return console.error;
}
return console.log;
/*eslint-enable*/
}

private static prefixByLevel(level: LogLevel): string {
switch (level) {
case LogLevel.Trace:
return "TRACE";
case LogLevel.Debug:
return "DEBUG";
case LogLevel.Info:
return "INFO";
case LogLevel.Warn:
return "WARN";
case LogLevel.Error:
return "ERROR";
}
return "???";
}

private static storeLog(level: LogLevel, message: string, data: object) {
if (!browser) {
return;
}

const logs = Logger.data();

while (logs.length >= MAX_LOCAL_STORAGE_LOGS) {
logs.shift();
}

const timestamp = Date.now();

logs.push({
data,
level,
message,
timestamp,
});

localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(logs));
}

public static data() {
if (!browser) {
return [];
}
return JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY) ?? "[]");
}
}

const log = new Logger();
export default log;
Loading