Resolve "Add optional donation/contribution link in-app"

environments/review-front-927-m6zslj/deployments/2768
Eliot Berriot 2019-09-23 11:30:25 +02:00
rodzic 5936dfc2bf
commit 01223afa80
17 zmienionych plików z 312 dodań i 8 usunięć

Wyświetl plik

@ -884,3 +884,7 @@ FEDERATION_OBJECT_FETCH_DELAY = env.int(
MODERATION_EMAIL_NOTIFICATIONS_ENABLED = env.bool(
"MODERATION_EMAIL_NOTIFICATIONS_ENABLED", default=True
)
# Delay in days after signup before we show the "support us" messages
INSTANCE_SUPPORT_MESSAGE_DELAY = env.int("INSTANCE_SUPPORT_MESSAGE_DELAY", default=15)
FUNKWHALE_SUPPORT_MESSAGE_DELAY = env.int("FUNKWHALE_SUPPORT_MESSAGE_DELAY", default=15)

Wyświetl plik

@ -82,6 +82,35 @@ class InstanceContactEmail(types.StringPreference):
field_kwargs = {"required": False}
@global_preferences_registry.register
class InstanceSupportMessage(types.StringPreference):
show_in_api = True
section = instance
name = "support_message"
verbose_name = "Support message"
default = ""
help_text = (
"A short message that will be displayed periodically to local users. "
"Use it to ask for financial support or anything else you might need. "
"(markdown allowed)."
)
widget = widgets.Textarea
field_kwargs = {"required": False}
@global_preferences_registry.register
class InstanceFunkwhaleSupportMessageEnabled(types.BooleanPreference):
show_in_api = True
section = instance
name = "funkwhale_support_message_enabled"
verbose_name = "Funkwhale Support message"
default = True
help_text = (
"If this is enabled, we will periodically display a message to encourage "
"local users to support Funkwhale."
)
@global_preferences_registry.register
class RavenDSN(types.StringPreference):
show_in_api = True

Wyświetl plik

@ -63,6 +63,10 @@ def get():
{"type": t, "label": l, "anonymous": t in unauthenticated_report_types}
for t, l in moderation_models.REPORT_TYPES
],
"funkwhaleSupportMessageEnabled": all_preferences.get(
"instance__funkwhale_support_message_enabled"
),
"instanceSupportMessage": all_preferences.get("instance__support_message"),
},
}

Wyświetl plik

@ -90,6 +90,15 @@ class UserAdmin(AuthUserAdmin):
},
),
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
(
_("Other"),
{
"fields": (
"instance_support_message_display_date",
"funkwhale_support_message_display_date",
)
},
),
(_("Useless fields"), {"fields": ("user_permissions", "groups")}),
)

Wyświetl plik

@ -0,0 +1,47 @@
# Generated by Django 2.2.4 on 2019-09-20 08:57
import datetime
from django.conf import settings
from django.db import migrations, models
import django.utils.timezone
import funkwhale_api.users.models
def set_display_date(apps, schema_editor):
"""
Set display date for instance/funkwhale support message on existing users
"""
User = apps.get_model("users", "User")
now = django.utils.timezone.now()
instance_support_message_display_date = now + datetime.timedelta(days=settings.INSTANCE_SUPPORT_MESSAGE_DELAY)
funkwhale_support_message_display_date = now + datetime.timedelta(days=settings.FUNKWHALE_SUPPORT_MESSAGE_DELAY)
User.objects.update(instance_support_message_display_date=instance_support_message_display_date)
User.objects.update(funkwhale_support_message_display_date=funkwhale_support_message_display_date)
def rewind(*args, **kwargs):
pass
class Migration(migrations.Migration):
dependencies = [
('users', '0015_application_scope'),
]
operations = [
migrations.AddField(
model_name='user',
name='funkwhale_support_message_display_date',
field=models.DateTimeField(blank=True, null=True, default=funkwhale_api.users.models.get_default_funkwhale_support_message_display_date),
),
migrations.AddField(
model_name='user',
name='instance_support_message_display_date',
field=models.DateTimeField(blank=True, null=True, default=funkwhale_api.users.models.get_default_instance_support_message_display_date),
),
migrations.RunPython(set_display_date, rewind),
]

Wyświetl plik

@ -82,6 +82,18 @@ PERMISSIONS = sorted(PERMISSIONS_CONFIGURATION.keys())
get_file_path = common_utils.ChunkedPath("users/avatars", preserve_file_name=False)
def get_default_instance_support_message_display_date():
return timezone.now() + datetime.timedelta(
days=settings.INSTANCE_SUPPORT_MESSAGE_DELAY
)
def get_default_funkwhale_support_message_display_date():
return timezone.now() + datetime.timedelta(
days=settings.FUNKWHALE_SUPPORT_MESSAGE_DELAY
)
@python_2_unicode_compatible
class User(AbstractUser):
@ -149,6 +161,15 @@ class User(AbstractUser):
upload_quota = models.PositiveIntegerField(null=True, blank=True)
instance_support_message_display_date = models.DateTimeField(
default=get_default_instance_support_message_display_date, null=True, blank=True
)
funkwhale_support_message_display_date = models.DateTimeField(
default=get_default_funkwhale_support_message_display_date,
null=True,
blank=True,
)
def __str__(self):
return self.username

Wyświetl plik

@ -109,7 +109,13 @@ class UserWriteSerializer(serializers.ModelSerializer):
class Meta:
model = models.User
fields = ["name", "privacy_level", "avatar"]
fields = [
"name",
"privacy_level",
"avatar",
"instance_support_message_display_date",
"funkwhale_support_message_display_date",
]
class UserReadSerializer(serializers.ModelSerializer):
@ -146,7 +152,11 @@ class MeSerializer(UserReadSerializer):
quota_status = serializers.SerializerMethodField()
class Meta(UserReadSerializer.Meta):
fields = UserReadSerializer.Meta.fields + ["quota_status"]
fields = UserReadSerializer.Meta.fields + [
"quota_status",
"instance_support_message_display_date",
"funkwhale_support_message_display_date",
]
def get_quota_status(self, o):
return o.get_quota_status() if o.actor else 0

Wyświetl plik

@ -87,6 +87,10 @@ def test_nodeinfo_dump(preferences, mocker, avatar):
},
{"type": "other", "label": "Other", "anonymous": True},
],
"funkwhaleSupportMessageEnabled": preferences[
"instance__funkwhale_support_message_enabled"
],
"instanceSupportMessage": preferences["instance__support_message"],
},
}
assert nodeinfo.get() == expected
@ -151,6 +155,10 @@ def test_nodeinfo_dump_stats_disabled(preferences, mocker):
},
{"type": "other", "label": "Other", "anonymous": True},
],
"funkwhaleSupportMessageEnabled": preferences[
"instance__funkwhale_support_message_enabled"
],
"instanceSupportMessage": preferences["instance__support_message"],
},
}
assert nodeinfo.get() == expected

Wyświetl plik

@ -220,3 +220,20 @@ def test_user_get_quota_status(factories, preferences, mocker):
"errored": 3,
"finished": 4,
}
@pytest.mark.parametrize(
"setting_name, field",
[
("INSTANCE_SUPPORT_MESSAGE_DELAY", "instance_support_message_display_date"),
("FUNKWHALE_SUPPORT_MESSAGE_DELAY", "funkwhale_support_message_display_date"),
],
)
def test_creating_user_set_support_display_date(
setting_name, field, settings, factories, now
):
setattr(settings, setting_name, 66) # display message every 66 days
expected = now + datetime.timedelta(days=66)
user = factories["users.User"]()
assert getattr(user, field) == expected

Wyświetl plik

@ -0,0 +1 @@
Added periodical message to incite people to support their pod and Funkwhale (#839)

Wyświetl plik

@ -113,6 +113,16 @@ your pod.
If you want to enable this feature on your pod, or learn more, please refer to `our documentation <https://docs.funkwhale.audio/moderator/listing.html>`_!
Periodic message to incite people to support their pod and Funkwhale
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Users will now be reminded on a regular basis that they can help Funkwhale by donating or contributing.
If specified by the pod admin, a separate and custom message will also be displayed in a similar way to provide instructions and links to support the pod.
Both messages will appear for the first time 15 days after signup, in the notifications tab. For each message, users can schedule a reminder for a later time, or disable the messages entirely.
Replaced Daphne by Gunicorn/Uvicorn [manual action required, non-docker only]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Wyświetl plik

@ -46,9 +46,9 @@
<i class="feed icon"></i>
<translate translate-context="*/Notifications/*">Notifications</translate>
<div
v-if="$store.state.ui.notifications.inbox > 0"
v-if="$store.state.ui.notifications.inbox + additionalNotifications > 0"
:class="['ui', 'teal', 'label']">
{{ $store.state.ui.notifications.inbox }}</div>
{{ $store.state.ui.notifications.inbox + additionalNotifications }}</div>
</router-link>
<router-link class="item" v-if="$store.state.auth.authenticated" :to="{name: 'logout'}"><i class="sign out icon"></i><translate translate-context="Sidebar/Login/List item.Link/Verb">Logout</translate></router-link>
<template v-else>
@ -186,7 +186,7 @@
</template>
<script>
import { mapState, mapActions } from "vuex"
import { mapState, mapActions, mapGetters } from "vuex"
import Player from "@/components/audio/Player"
import Logo from "@/components/Logo"
@ -219,6 +219,9 @@ export default {
}
},
computed: {
...mapGetters({
additionalNotifications: "ui/additionalNotifications",
}),
...mapState({
queue: state => state.queue,
url: state => state.route.path

Wyświetl plik

@ -1,7 +1,9 @@
import Vue from 'vue'
import axios from 'axios'
import jwtDecode from 'jwt-decode'
import logger from '@/logging'
import router from '@/router'
import lodash from '@/lodash'
export default {
namespaced: true,
@ -73,6 +75,11 @@ export default {
},
permission: (state, {key, status}) => {
state.availablePermissions[key] = status
},
profilePartialUpdate: (state, payload) => {
lodash.keys(payload).forEach((k) => {
Vue.set(state.profile, k, payload[k])
})
}
},
actions: {

Wyświetl plik

@ -28,6 +28,12 @@ export default {
},
long_description: {
value: ''
},
funkwhale_support_message_enabled: {
value: true
},
support_message: {
value: ''
}
},
users: {

Wyświetl plik

@ -26,6 +26,44 @@ export default {
},
pageTitle: null
},
getters: {
showInstanceSupportMessage: (state, getters, rootState) => {
if (!rootState.auth.profile) {
return false
}
if (!rootState.instance.settings.instance.support_message.value) {
return false
}
let displayDate = rootState.auth.profile.instance_support_message_display_date
if (!displayDate) {
return false
}
return moment(displayDate) < moment(state.lastDate)
},
showFunkwhaleSupportMessage: (state, getters, rootState) => {
if (!rootState.auth.profile) {
return false
}
if (!rootState.instance.settings.instance.funkwhale_support_message_enabled.value) {
return false
}
let displayDate = rootState.auth.profile.funkwhale_support_message_display_date
if (!displayDate) {
return false
}
return moment(displayDate) < moment(state.lastDate)
},
additionalNotifications: (state, getters) => {
let count = 0
if (getters.showInstanceSupportMessage) {
count += 1
}
if (getters.showFunkwhaleSupportMessage) {
count += 1
}
return count
}
},
mutations: {
addWebsocketEventHandler: (state, {eventName, id, handler}) => {
state.websocketEventsHandlers[eventName][id] = handler

Wyświetl plik

@ -2,6 +2,71 @@
<main class="main pusher" v-title="labels.title">
<section class="ui vertical aligned stripe segment">
<div class="ui container">
<div class="ui container" v-if="additionalNotifications">
<h1 class="ui header"><translate translate-context="Content/Notifications/Title">Your messages</translate></h1>
<div class="ui two column stackable grid">
<div class="column" v-if="showInstanceSupportMessage">
<div class="ui attached info message">
<div class="header">
<translate translate-context="Content/Notifications/Header">Support this Funkwhale pod</translate>
</div>
<div v-html="markdown.makeHtml($store.state.instance.settings.instance.support_message.value)"></div>
</div>
<div class="ui bottom attached segment">
<form @submit.prevent="setDisplayDate('instance_support_message_display_date', instanceSupportMessageDelay)" class="ui inline form">
<div class="inline field">
<label>
<translate translate-context="Content/Notifications/Label">Remind me in:</translate>
</label>
<select v-model="instanceSupportMessageDelay">
<option :value="30"><translate translate-context="*/*/*">30 days</translate></option>
<option :value="60"><translate translate-context="*/*/*">60 days</translate></option>
<option :value="90"><translate translate-context="*/*/*">90 days</translate></option>
<option :value="null"><translate translate-context="*/*/*">Never</translate></option>
</select>
<button type="submit" class="ui right floated basic button">
<translate translate-context="Content/Notifications/Button.Label">Got it!</translate>
</button>
</div>
</form>
</div>
</div>
<div class="column" v-if="showFunkwhaleSupportMessage">
<div class="ui info attached message">
<div class="header">
<translate translate-context="Content/Notifications/Header">Do you like Funkwhale?</translate>
</div>
<p>
<translate translate-context="Content/Notifications/Paragraph">We noticed you've been here for a while. If Funkwhale is useful to you, we could use your help to make it even better!</translate>
</p>
<a href="https://funkwhale.audio/support-us" _target="blank" rel="noopener" class="ui primary inverted button">
<translate translate-context="Content/Notifications/Button.Label/Verb">Donate</translate>
</a>
<a href="https://contribute.funkwhale.audio" _target="blank" rel="noopener" class="ui secondary inverted button">
<translate translate-context="Content/Notifications/Button.Label/Verb">Discover other ways to help</translate>
</a>
</div>
<div class="ui bottom attached segment">
<form @submit.prevent="setDisplayDate('funkwhale_support_message_display_date', funkwhaleSupportMessageDelay)" class="ui inline form">
<div class="inline field">
<label>
<translate translate-context="Content/Notifications/Label">Remind me in:</translate>
</label>
<select v-model="funkwhaleSupportMessageDelay">
<option :value="30"><translate translate-context="*/*/*">30 days</translate></option>
<option :value="60"><translate translate-context="*/*/*">60 days</translate></option>
<option :value="90"><translate translate-context="*/*/*">90 days</translate></option>
<option :value="null"><translate translate-context="*/*/*">Never</translate></option>
</select>
<button type="submit" class="ui right floated basic button">
<translate translate-context="Content/Notifications/Button.Label">Got it!</translate>
</button>
</div>
</form>
</div>
</div>
</div>
</div>
<h1 class="ui header"><translate translate-context="Content/Notifications/Title">Your notifications</translate></h1>
<div class="ui toggle checkbox">
<input v-model="filters.is_read" type="checkbox">
@ -25,7 +90,7 @@
<notification-row :item="item" v-for="item in notifications.results" :key="item.id" />
</tbody>
</table>
<p v-else>
<p v-else-if="additionalNotifications === 0">
<translate translate-context="Content/Notifications/Paragraph">No notification to show.</translate>
</p>
</div>
@ -34,9 +99,11 @@
</template>
<script>
import { mapState } from "vuex"
import { mapState, mapGetters } from "vuex"
import axios from "axios"
import logger from "@/logging"
import showdown from 'showdown'
import moment from 'moment'
import NotificationRow from "@/components/notifications/NotificationRow"
@ -44,7 +111,10 @@ export default {
data() {
return {
isLoading: false,
markdown: new showdown.Converter(),
notifications: {count: 0, results: []},
instanceSupportMessageDelay: 60,
funkwhaleSupportMessageDelay: 60,
filters: {
is_read: false
}
@ -71,6 +141,11 @@ export default {
...mapState({
events: state => state.instance.events
}),
...mapGetters({
additionalNotifications: 'ui/additionalNotifications',
showInstanceSupportMessage: 'ui/showInstanceSupportMessage',
showFunkwhaleSupportMessage: 'ui/showFunkwhaleSupportMessage',
}),
labels() {
return {
title: this.$pgettext('*/Notifications/*', "Notifications")
@ -82,6 +157,20 @@ export default {
this.notifications.count += 1
this.notifications.results.unshift(event.item)
},
setDisplayDate (field, days) {
let payload = {}
let newDisplayDate
if (days) {
newDisplayDate = moment().add({days})
} else {
newDisplayDate = null
}
payload[field] = newDisplayDate
let self = this
axios.patch(`users/users/${this.$store.state.auth.username}/`, payload).then((response) => {
self.$store.commit('auth/profilePartialUpdate', response.data)
})
},
fetch(params) {
this.isLoading = true
let self = this

Wyświetl plik

@ -99,6 +99,7 @@ export default {
"instance__rules",
"instance__terms",
"instance__banner",
"instance__support_message"
]
},
{
@ -152,7 +153,7 @@ export default {
{
label: uiLabel,
id: "ui",
settings: ["ui__custom_css"]
settings: ["ui__custom_css", "instance__funkwhale_support_message_enabled"]
},
{
label: statisticsLabel,