You've already forked AstralRinth
forked from didirus/AstralRinth
Improve ad security, add CMP changes (#2399)
This commit is contained in:
@@ -4,7 +4,7 @@ import { get as getCreds } from '@/helpers/mr_auth.js'
|
|||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { get_user } from '@/helpers/cache.js'
|
import { get_user } from '@/helpers/cache.js'
|
||||||
import { ChevronRightIcon } from '@modrinth/assets'
|
import { ChevronRightIcon } from '@modrinth/assets'
|
||||||
import { init_ads_window } from '@/helpers/ads.js'
|
import { init_ads_window, open_ads_link, record_ads_click } from '@/helpers/ads.js'
|
||||||
import { listen } from '@tauri-apps/api/event'
|
import { listen } from '@tauri-apps/api/event'
|
||||||
|
|
||||||
const showAd = ref(true)
|
const showAd = ref(true)
|
||||||
@@ -73,6 +73,11 @@ function updateAdPosition(overrideShown = false) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function openPlusLink() {
|
||||||
|
await record_ads_click()
|
||||||
|
await open_ads_link('https://modrinth.com/plus', 'https://modrinth.com')
|
||||||
|
}
|
||||||
|
|
||||||
const unlisten = await listen('ads-scroll', (event) => {
|
const unlisten = await listen('ads-scroll', (event) => {
|
||||||
if (adsWrapper.value) {
|
if (adsWrapper.value) {
|
||||||
adsWrapper.value.parentNode.scrollTop += event.payload.scroll
|
adsWrapper.value.parentNode.scrollTop += event.payload.scroll
|
||||||
@@ -105,17 +110,17 @@ onUnmounted(() => {
|
|||||||
class="ad-parent relative mb-3 flex w-full justify-center rounded-2xl bg-bg-raised cursor-pointer"
|
class="ad-parent relative mb-3 flex w-full justify-center rounded-2xl bg-bg-raised cursor-pointer"
|
||||||
>
|
>
|
||||||
<div class="flex max-h-[250px] min-h-[250px] min-w-[300px] max-w-[300px] flex-col gap-4 p-6">
|
<div class="flex max-h-[250px] min-h-[250px] min-w-[300px] max-w-[300px] flex-col gap-4 p-6">
|
||||||
<p class="m-0 text-2xl font-bold text-contrast">90% of ad revenue goes to creators</p>
|
<p class="m-0 text-2xl font-bold text-contrast">75% of ad revenue goes to creators</p>
|
||||||
<a
|
<button
|
||||||
href="https://modrinth.com/plus"
|
class="mt-auto items-center gap-1 text-purple hover:underline bg-transparent border-none text-left cursor-pointer outline-none"
|
||||||
class="mt-auto items-center gap-1 text-purple hover:underline"
|
@click="openPlusLink"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
Support creators and Modrinth ad-free with
|
Support creators and Modrinth ad-free with
|
||||||
<span class="font-bold">Modrinth+</span>
|
<span class="font-bold">Modrinth+</span>
|
||||||
</span>
|
</span>
|
||||||
<ChevronRightIcon class="relative top-[3px] h-5 w-5" />
|
<ChevronRightIcon class="relative top-[3px] h-5 w-5" />
|
||||||
</a>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -11,3 +11,11 @@ export async function show_ads_window() {
|
|||||||
export async function hide_ads_window(reset) {
|
export async function hide_ads_window(reset) {
|
||||||
return await invoke('plugin:ads|hide_ads_window', { reset })
|
return await invoke('plugin:ads|hide_ads_window', { reset })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function record_ads_click() {
|
||||||
|
return await invoke('plugin:ads|record_ads_click')
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function open_ads_link(path, origin) {
|
||||||
|
return await invoke('plugin:ads|open_link', { path, origin })
|
||||||
|
}
|
||||||
|
|||||||
@@ -226,6 +226,8 @@ fn main() {
|
|||||||
"hide_ads_window",
|
"hide_ads_window",
|
||||||
"scroll_ads_window",
|
"scroll_ads_window",
|
||||||
"show_ads_window",
|
"show_ads_window",
|
||||||
|
"record_ads_click",
|
||||||
|
"open_link",
|
||||||
])
|
])
|
||||||
.default_permission(
|
.default_permission(
|
||||||
DefaultPermissionRule::AllowAllCommands,
|
DefaultPermissionRule::AllowAllCommands,
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
"ads-window"
|
"ads-window"
|
||||||
],
|
],
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"shell:allow-open",
|
|
||||||
"ads:default"
|
"ads:default"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
{"ads":{"identifier":"ads","description":"","remote":{"urls":["https://modrinth.com/*","http://localhost:3000/*"]},"local":false,"webviews":["ads-window"],"permissions":["shell:allow-open","ads:default"]},"core":{"identifier":"core","description":"","local":true,"windows":["main"],"permissions":["core:default","core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","core:window:allow-create","core:window:allow-maximize","core:window:allow-toggle-maximize","core:window:allow-unmaximize","core:window:allow-minimize","core:window:allow-unminimize","core:window:allow-show","core:window:allow-hide","core:window:allow-close","core:window:allow-set-decorations","core:window:allow-start-dragging","core:webview:allow-set-webview-zoom"]},"plugins":{"identifier":"plugins","description":"","local":true,"windows":["main"],"permissions":["dialog:allow-open","dialog:allow-confirm","shell:allow-open","os:allow-platform","os:allow-version","os:allow-os-type","os:allow-family","os:allow-arch","os:allow-exe-extension","os:allow-locale","os:allow-hostname","deep-link:default","window-state:default","window-state:allow-restore-state","window-state:allow-save-window-state","auth:default","import:default","jre:default","logs:default","metadata:default","mr-auth:default","profile-create:default","pack:default","process:default","profile:default","cache:default","settings:default","tags:default","utils:default","ads:default"]},"updater":{"identifier":"updater","description":"","local":true,"windows":["main"],"permissions":["updater:default"]}}
|
{"ads":{"identifier":"ads","description":"","remote":{"urls":["https://modrinth.com/*","http://localhost:3000/*"]},"local":false,"webviews":["ads-window"],"permissions":["ads:default"]},"core":{"identifier":"core","description":"","local":true,"windows":["main"],"permissions":["core:default","core:path:default","core:event:default","core:window:default","core:app:default","core:resources:default","core:menu:default","core:tray:default","core:window:allow-create","core:window:allow-maximize","core:window:allow-toggle-maximize","core:window:allow-unmaximize","core:window:allow-minimize","core:window:allow-unminimize","core:window:allow-show","core:window:allow-hide","core:window:allow-close","core:window:allow-set-decorations","core:window:allow-start-dragging","core:webview:allow-set-webview-zoom"]},"plugins":{"identifier":"plugins","description":"","local":true,"windows":["main"],"permissions":["dialog:allow-open","dialog:allow-confirm","shell:allow-open","os:allow-platform","os:allow-version","os:allow-os-type","os:allow-family","os:allow-arch","os:allow-exe-extension","os:allow-locale","os:allow-hostname","deep-link:default","window-state:default","window-state:allow-restore-state","window-state:allow-save-window-state","auth:default","import:default","jre:default","logs:default","metadata:default","mr-auth:default","profile-create:default","pack:default","process:default","profile:default","cache:default","settings:default","tags:default","utils:default","ads:default"]},"updater":{"identifier":"updater","description":"","local":true,"windows":["main"],"permissions":["updater:default"]}}
|
||||||
@@ -320,6 +320,20 @@
|
|||||||
"ads:allow-init-ads-window"
|
"ads:allow-init-ads-window"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "ads:allow-open-link -> Enables the open_link command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ads:allow-open-link"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "ads:allow-record-ads-click -> Enables the record_ads_click command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ads:allow-record-ads-click"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "ads:allow-scroll-ads-window -> Enables the scroll_ads_window command without any pre-configured scope.",
|
"description": "ads:allow-scroll-ads-window -> Enables the scroll_ads_window command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -348,6 +362,20 @@
|
|||||||
"ads:deny-init-ads-window"
|
"ads:deny-init-ads-window"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "ads:deny-open-link -> Denies the open_link command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ads:deny-open-link"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "ads:deny-record-ads-click -> Denies the record_ads_click command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ads:deny-record-ads-click"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "ads:deny-scroll-ads-window -> Denies the scroll_ads_window command without any pre-configured scope.",
|
"description": "ads:deny-scroll-ads-window -> Denies the scroll_ads_window command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -320,6 +320,20 @@
|
|||||||
"ads:allow-init-ads-window"
|
"ads:allow-init-ads-window"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "ads:allow-open-link -> Enables the open_link command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ads:allow-open-link"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "ads:allow-record-ads-click -> Enables the record_ads_click command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ads:allow-record-ads-click"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "ads:allow-scroll-ads-window -> Enables the scroll_ads_window command without any pre-configured scope.",
|
"description": "ads:allow-scroll-ads-window -> Enables the scroll_ads_window command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -348,6 +362,20 @@
|
|||||||
"ads:deny-init-ads-window"
|
"ads:deny-init-ads-window"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "ads:deny-open-link -> Denies the open_link command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ads:deny-open-link"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "ads:deny-record-ads-click -> Denies the record_ads_click command without any pre-configured scope.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ads:deny-record-ads-click"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "ads:deny-scroll-ads-window -> Denies the scroll_ads_window command without any pre-configured scope.",
|
"description": "ads:deny-scroll-ads-window -> Denies the scroll_ads_window command without any pre-configured scope.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
if (!window.modrinthClickListener) {
|
document.addEventListener(
|
||||||
window.modrinthClickListener = true
|
'click',
|
||||||
document.addEventListener('click', function (e) {
|
function (e) {
|
||||||
|
window.top.postMessage({ modrinthAdClick: true }, 'https://modrinth.com')
|
||||||
|
|
||||||
let target = e.target
|
let target = e.target
|
||||||
while (target != null) {
|
while (target != null) {
|
||||||
if (target.matches('a')) {
|
if (target.matches('a')) {
|
||||||
@@ -12,8 +14,9 @@ if (!window.modrinthClickListener) {
|
|||||||
}
|
}
|
||||||
target = target.parentElement
|
target = target.parentElement
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
}
|
true,
|
||||||
|
)
|
||||||
|
|
||||||
window.open = (url, target, features) => {
|
window.open = (url, target, features) => {
|
||||||
window.top.postMessage({ modrinthOpenUrl: url }, 'https://modrinth.com')
|
window.top.postMessage({ modrinthOpenUrl: url }, 'https://modrinth.com')
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
use tauri::plugin::TauriPlugin;
|
use tauri::plugin::TauriPlugin;
|
||||||
use tauri::{Emitter, LogicalPosition, LogicalSize, Manager, Runtime};
|
use tauri::{
|
||||||
|
Emitter, Listener, LogicalPosition, LogicalSize, Manager, Runtime,
|
||||||
|
};
|
||||||
|
use tauri_plugin_shell::{open, ShellExt};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
pub struct AdsState {
|
pub struct AdsState {
|
||||||
pub shown: bool,
|
pub shown: bool,
|
||||||
pub size: Option<LogicalSize<f32>>,
|
pub size: Option<LogicalSize<f32>>,
|
||||||
pub position: Option<LogicalPosition<f32>>,
|
pub position: Option<LogicalPosition<f32>>,
|
||||||
|
pub last_click: Option<Instant>,
|
||||||
|
pub malicious_origins: HashSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||||
@@ -16,6 +23,8 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
|||||||
shown: true,
|
shown: true,
|
||||||
size: None,
|
size: None,
|
||||||
position: None,
|
position: None,
|
||||||
|
last_click: None,
|
||||||
|
malicious_origins: HashSet::new(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// We refresh the ads window every 5 minutes for performance
|
// We refresh the ads window every 5 minutes for performance
|
||||||
@@ -43,6 +52,8 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
|||||||
hide_ads_window,
|
hide_ads_window,
|
||||||
scroll_ads_window,
|
scroll_ads_window,
|
||||||
show_ads_window,
|
show_ads_window,
|
||||||
|
record_ads_click,
|
||||||
|
open_link,
|
||||||
])
|
])
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
@@ -75,7 +86,7 @@ pub async fn init_ads_window<R: Runtime>(
|
|||||||
let _ = webview.set_size(LogicalSize::new(width, height));
|
let _ = webview.set_size(LogicalSize::new(width, height));
|
||||||
}
|
}
|
||||||
} else if let Some(window) = app.get_window("main") {
|
} else if let Some(window) = app.get_window("main") {
|
||||||
let _ = window.add_child(
|
let window = window.add_child(
|
||||||
tauri::webview::WebviewBuilder::new(
|
tauri::webview::WebviewBuilder::new(
|
||||||
"ads-window",
|
"ads-window",
|
||||||
WebviewUrl::External(
|
WebviewUrl::External(
|
||||||
@@ -93,6 +104,12 @@ pub async fn init_ads_window<R: Runtime>(
|
|||||||
},
|
},
|
||||||
LogicalSize::new(width, height),
|
LogicalSize::new(width, height),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if let Ok(window) = window {
|
||||||
|
window.listen_any("click", |event| {
|
||||||
|
println!("click: {:?}", event);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -159,3 +176,43 @@ pub async fn scroll_ads_window<R: Runtime>(
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn record_ads_click<R: Runtime>(
|
||||||
|
app: tauri::AppHandle<R>,
|
||||||
|
) -> crate::api::Result<()> {
|
||||||
|
let state = app.state::<RwLock<AdsState>>();
|
||||||
|
|
||||||
|
let mut state = state.write().await;
|
||||||
|
state.last_click = Some(Instant::now());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn open_link<R: Runtime>(
|
||||||
|
app: tauri::AppHandle<R>,
|
||||||
|
path: String,
|
||||||
|
origin: String,
|
||||||
|
) -> crate::api::Result<()> {
|
||||||
|
let state = app.state::<RwLock<AdsState>>();
|
||||||
|
let mut state = state.write().await;
|
||||||
|
|
||||||
|
if url::Url::parse(&path).is_ok()
|
||||||
|
&& !state.malicious_origins.contains(&origin)
|
||||||
|
{
|
||||||
|
if let Some(last_click) = state.last_click {
|
||||||
|
if last_click.elapsed() < Duration::from_millis(100) {
|
||||||
|
let _ = app.shell().open(&path, None);
|
||||||
|
state.last_click = None;
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::info!("Malicious click: {path} origin {origin}");
|
||||||
|
state.malicious_origins.insert(origin);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="ad-parent relative mb-3 flex w-full justify-center rounded-2xl bg-bg-raised">
|
<div class="ad-parent relative mb-3 flex w-full justify-center rounded-2xl bg-bg-raised">
|
||||||
<div class="flex max-h-[250px] min-h-[250px] min-w-[300px] max-w-[300px] flex-col gap-4 p-6">
|
<div class="flex max-h-[250px] min-h-[250px] min-w-[300px] max-w-[300px] flex-col gap-4 p-6">
|
||||||
<p class="m-0 text-2xl font-bold text-contrast">90% of ad revenue goes to creators</p>
|
<p class="m-0 text-2xl font-bold text-contrast">75% of ad revenue goes to creators</p>
|
||||||
<nuxt-link to="/plus" class="mt-auto items-center gap-1 text-purple hover:underline">
|
<nuxt-link to="/plus" class="mt-auto items-center gap-1 text-purple hover:underline">
|
||||||
<span>
|
<span>
|
||||||
Support creators and Modrinth ad-free with
|
Support creators and Modrinth ad-free with
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
You have
|
You have
|
||||||
<strong>{{ $formatMoney(userBalance.available) }}</strong>
|
<strong>{{ $formatMoney(userBalance.available) }}</strong>
|
||||||
available to withdraw. <strong>{{ $formatMoney(userBalance.pending) }}</strong> of your
|
available to withdraw. <strong>{{ $formatMoney(userBalance.pending) }}</strong> of your
|
||||||
balance is <a class="text-link" href="#">pending</a>.
|
balance is <nuxt-link class="text-link" to="/legal/cmp-info#pending">pending</nuxt-link>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p v-else>
|
<p v-else>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
<strong>{{ $formatMoney(userBalance.available) }}</strong
|
<strong>{{ $formatMoney(userBalance.available) }}</strong
|
||||||
>, which is under the minimum of ${{ minWithdraw }} to withdraw.
|
>, which is under the minimum of ${{ minWithdraw }} to withdraw.
|
||||||
<strong>{{ $formatMoney(userBalance.pending) }}</strong> of your balance is
|
<strong>{{ $formatMoney(userBalance.pending) }}</strong> of your balance is
|
||||||
<a class="text-link" href="#">pending</a>.
|
<nuxt-link class="text-link" to="/legal/cmp-info#pending">pending</nuxt-link>.
|
||||||
</p>
|
</p>
|
||||||
<div class="input-group mt-4">
|
<div class="input-group mt-4">
|
||||||
<nuxt-link
|
<nuxt-link
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="markdown-body">
|
<div class="markdown-body">
|
||||||
<h1>Rewards Program Information</h1>
|
<h1>Rewards Program Information</h1>
|
||||||
<p><em>Last modified: May 13, 2024</em></p>
|
<p><em>Last modified: Sep 12, 2024</em></p>
|
||||||
<p>
|
<p>
|
||||||
This page was created for transparency for how the rewards program works on Modrinth. Feel
|
This page was created for transparency for how the rewards program works on Modrinth. Feel
|
||||||
free to join our Discord or email
|
free to join our Discord or email
|
||||||
@@ -15,10 +15,11 @@
|
|||||||
<h2>Rewards Distribution</h2>
|
<h2>Rewards Distribution</h2>
|
||||||
<p>
|
<p>
|
||||||
We collect ad revenue on our website and app through our ad network
|
We collect ad revenue on our website and app through our ad network
|
||||||
<a href="https://adrinth.com">Adrinth</a>. We then distribute this ad revenue to creators.
|
<a href="https://adrinth.com">Adrinth</a>, which is powered by
|
||||||
|
<a href="https://aditude.io">Aditude</a>. We then distribute this ad revenue to creators.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
The advertising revenue of the entire website and app is split 90% to creators and 10% to
|
The advertising revenue of the entire website and app is split 75% to creators and 25% to
|
||||||
Modrinth.
|
Modrinth.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
@@ -42,10 +43,10 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<p>In this scenario, the earnings for each creator and Modrinth would be as follows:</p>
|
<p>In this scenario, the earnings for each creator and Modrinth would be as follows:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Modrinth: $10 (10% of $100, the site's earnings for the day)</li>
|
<li>Modrinth: $25 (25% of $100, the site's earnings for the day)</li>
|
||||||
<li>User A: $58.69 ($90 * (10 + 30 + 100 + 10)/230)</li>
|
<li>User A: $48.91 ($75 * (10 + 30 + 100 + 10)/230)</li>
|
||||||
<li>User B: $12.52 (0.4 * $90 * (50 + 20 + 10 + 0)/230)</li>
|
<li>User B: $10.43 (0.4 * $75 * (50 + 20 + 10 + 0)/230)</li>
|
||||||
<li>User C: $18.78 (0.6 * $90 * (50 + 20 + 10 + 0)/230)</li>
|
<li>User C: $15.65 (0.6 * $75 * (50 + 20 + 10 + 0)/230)</li>
|
||||||
<li>Note: 230 is the sum of all page views and in-app downloads from above</li>
|
<li>Note: 230 is the sum of all page views and in-app downloads from above</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p>
|
||||||
@@ -72,19 +73,62 @@
|
|||||||
</p>
|
</p>
|
||||||
<h3>What methods can I use withdraw money from my account? Are there any fees?</h3>
|
<h3>What methods can I use withdraw money from my account? Are there any fees?</h3>
|
||||||
<p>
|
<p>
|
||||||
Right now, you can use PayPal or Venmo to withdraw money from your Modrinth account. We are
|
Right now, you can use PayPal or Venmo to withdraw money from your Modrinth account. Gift card
|
||||||
working on more methods to withdraw money from your account. There are fees to withdraw money
|
withdrawal is also available. We are working on more methods to withdraw money from your
|
||||||
from your Modrinth account—see the revenue page in your dashboard for more information.
|
account. There are fees to withdraw money from your Modrinth account—see the revenue page in
|
||||||
|
your dashboard for more information.
|
||||||
</p>
|
</p>
|
||||||
<h3>Modrinth used to give 100% of project page revenue to creators. What changed?</h3>
|
<h3 id="pending">What does "pending" revenue mean in my dashboard?</h3>
|
||||||
<p>
|
<p>
|
||||||
While this is true, our new system (as of 08/05/23) gives more of the site's revenue to
|
Modrinth receives ad revenue from our ad providers on a NET 60 day basis. Due to this, not all
|
||||||
creators, so creators will earn more. In the old system, we would earn revenue through
|
revenue is not immediately available to withdraw. We pay creators as soon as we receive the
|
||||||
advertisements in search and user profile pages. This amounted on average each month to about
|
money from our ad providers, which is 60 days after the last day of each month. This table
|
||||||
15-20% of the site's total advertising revenue (so a 80-85% split to creators). The new system
|
outlines some example dates of how NET 60 payments are made:
|
||||||
gives creators more revenue and a more favorable split towards creators (90%).
|
</p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Payment available date</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>January 1st</td>
|
||||||
|
<td>March 31st</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>January 15th</td>
|
||||||
|
<td>March 31st</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>March 3rd</td>
|
||||||
|
<td>May 30th</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>June 30th</td>
|
||||||
|
<td>August 29th</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>July 14th</td>
|
||||||
|
<td>September 29th</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>October 12th</td>
|
||||||
|
<td>December 30th</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<h3>How do I know Modrinth is being transparent about revenue?</h3>
|
||||||
|
<p>
|
||||||
|
We aim to be as transparent as possible with creator revenue. All of our code is open source,
|
||||||
|
including our
|
||||||
|
<a href="https://github.com/modrinth/labrinth/blob/master/src/queue/payouts.rs#L561">
|
||||||
|
revenue distribution system </a
|
||||||
|
>. We also have an
|
||||||
|
<a href="https://api.modrinth.com/v3/payout/platform_revenue">API route</a> that allows users
|
||||||
|
to query exact daily revenue for the site.
|
||||||
</p>
|
</p>
|
||||||
<h3></h3>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
top: 0;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -58,19 +58,18 @@
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
let lastUrl = null
|
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
"message",
|
"message",
|
||||||
(event) => {
|
(event) => {
|
||||||
if (event.data.modrinthOpenUrl && window.__TAURI_INTERNALS__ && lastUrl !== event.data.modrinthOpenUrl) {
|
if (event.data.modrinthAdClick && window.__TAURI_INTERNALS__) {
|
||||||
lastUrl = event.data.modrinthOpenUrl
|
window.__TAURI_INTERNALS__.invoke("plugin:ads|record_ads_click", {});
|
||||||
// window.__TAURI_INTERNALS__.invoke("plugin:shell|open", {
|
}
|
||||||
// path: event.data.modrinthOpenUrl,
|
|
||||||
// });
|
|
||||||
|
|
||||||
setTimeout(() => {
|
if (event.data.modrinthOpenUrl && window.__TAURI_INTERNALS__) {
|
||||||
lastUrl = null
|
window.__TAURI_INTERNALS__.invoke("plugin:ads|open_link", {
|
||||||
}, 500)
|
path: event.data.modrinthOpenUrl,
|
||||||
|
origin: event.origin,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
|
|||||||
@@ -1,121 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="GBBNWLJVGRHFLYVGSZKSSKNTHFYXHMBD">
|
|
||||||
<a href="https://modrinth.com/plus" rel="noopener nofollow sponsored" :target="target"></a>
|
|
||||||
<div class="GBBNWLJVGRHFLYVGSZKSSKNTHFYXHMBD-0">
|
|
||||||
<div class="GBBNWLJVGRHFLYVGSZKSSKNTHFYXHMBD-1">
|
|
||||||
<div class="GBBNWLJVGRHFLYVGSZKSSKNTHFYXHMBD-2">
|
|
||||||
<ModrinthPlusIcon class="GBBNWLJVGRHFLYVGSZKSSKNTHFYXHMBD-3" />
|
|
||||||
<span>
|
|
||||||
<span>
|
|
||||||
90% of ad revenue goes to creators. Go ad-free while supporting creators with
|
|
||||||
</span>
|
|
||||||
<strong>Modrinth Plus.</strong>
|
|
||||||
<span> Subscribe today!</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="GBBNWLJVGRHFLYVGSZKSSKNTHFYXHMBD-4">
|
|
||||||
<a rel="noopener sponsored" :target="target" href="https://adrinth.com"> Ad via Adrinth </a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { computed } from 'vue'
|
|
||||||
import { ModrinthPlusIcon } from '@modrinth/assets'
|
|
||||||
|
|
||||||
const props = withDefaults(
|
|
||||||
defineProps<{
|
|
||||||
external: boolean
|
|
||||||
}>(),
|
|
||||||
{
|
|
||||||
external: true,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
const target = computed(() => (props.external ? '_blank' : '_self'))
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.GBBNWLJVGRHFLYVGSZKSSKNTHFYXHMBD {
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: var(--gap-md);
|
|
||||||
background: var(--color-ad);
|
|
||||||
border: 1px solid var(--color-ad-raised);
|
|
||||||
border-radius: var(--radius-lg);
|
|
||||||
container-type: inline-size;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
> a {
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:has(> a:first-child:active) {
|
|
||||||
scale: 0.99;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.GBBNWLJVGRHFLYVGSZKSSKNTHFYXHMBD-0 {
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.3em;
|
|
||||||
}
|
|
||||||
.GBBNWLJVGRHFLYVGSZKSSKNTHFYXHMBD-1 {
|
|
||||||
color: var(--color-base);
|
|
||||||
padding: 1em;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
.GBBNWLJVGRHFLYVGSZKSSKNTHFYXHMBD-2 {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
color: var(--color-base);
|
|
||||||
margin-right: 7.5rem;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.GBBNWLJVGRHFLYVGSZKSSKNTHFYXHMBD-2 b,
|
|
||||||
.GBBNWLJVGRHFLYVGSZKSSKNTHFYXHMBD-2 strong {
|
|
||||||
color: var(--color-ad-highlight);
|
|
||||||
}
|
|
||||||
.GBBNWLJVGRHFLYVGSZKSSKNTHFYXHMBD-3 {
|
|
||||||
padding-top: 1px;
|
|
||||||
height: 1.5rem;
|
|
||||||
width: auto;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
.GBBNWLJVGRHFLYVGSZKSSKNTHFYXHMBD-4 a {
|
|
||||||
position: absolute;
|
|
||||||
bottom: -1px;
|
|
||||||
right: -1px;
|
|
||||||
text-align: center;
|
|
||||||
font-weight: 600;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 0.8em;
|
|
||||||
color: var(--color-ad-contrast);
|
|
||||||
background: var(--color-ad-raised);
|
|
||||||
letter-spacing: 0.1ch;
|
|
||||||
margin: 0;
|
|
||||||
padding: 2px 10px;
|
|
||||||
border-top-left-radius: var(--radius-lg);
|
|
||||||
border-bottom-right-radius: var(--radius-lg);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media screen and (max-width: 800px) {
|
|
||||||
.GBBNWLJVGRHFLYVGSZKSSKNTHFYXHMBD-2 {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
.GBBNWLJVGRHFLYVGSZKSSKNTHFYXHMBD-2 a {
|
|
||||||
align-items: flex-start;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -22,7 +22,6 @@ export { default as Page } from './base/Page.vue'
|
|||||||
export { default as Pagination } from './base/Pagination.vue'
|
export { default as Pagination } from './base/Pagination.vue'
|
||||||
export { default as PopoutMenu } from './base/PopoutMenu.vue'
|
export { default as PopoutMenu } from './base/PopoutMenu.vue'
|
||||||
export { default as ProjectCard } from './base/ProjectCard.vue'
|
export { default as ProjectCard } from './base/ProjectCard.vue'
|
||||||
export { default as Promotion } from './base/Promotion.vue'
|
|
||||||
export { default as ScrollablePanel } from './base/ScrollablePanel.vue'
|
export { default as ScrollablePanel } from './base/ScrollablePanel.vue'
|
||||||
export { default as Slider } from './base/Slider.vue'
|
export { default as Slider } from './base/Slider.vue'
|
||||||
export { default as StatItem } from './base/StatItem.vue'
|
export { default as StatItem } from './base/StatItem.vue'
|
||||||
|
|||||||
Reference in New Issue
Block a user