mirror of
https://github.com/pinry/pinry.git
synced 2025-11-16 18:05:51 +01:00
Feature: Add new public user-api and user-profile card in front-end
This commit is contained in:
137
pinry-spa/src/components/UserProfileCard.vue
Normal file
137
pinry-spa/src/components/UserProfileCard.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<div class="user-profile-card">
|
||||
<div id="user-home-container">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<div class="media">
|
||||
<div class="media-left">
|
||||
<figure class="image is-48x48">
|
||||
<b-skeleton width="48px" height="48px" :active="avatarLoading"></b-skeleton>
|
||||
<img
|
||||
@load="onAvatarLoaded"
|
||||
v-show="!avatarLoading"
|
||||
:src="user.avatar"
|
||||
alt="avatar"
|
||||
>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="media-content" v-show="!avatarLoading">
|
||||
<p class="title is-4">{{ user.username }}</p>
|
||||
<p class="subtitle is-6">@{{ location }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
Yet another Pinry user.
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<div class="tabs is-toggle">
|
||||
<ul>
|
||||
<li :class="trueFalse2Class(inPins)">
|
||||
<a @click="go2UserPins">
|
||||
<b-icon
|
||||
type="is-dark"
|
||||
icon="image"
|
||||
custom-size="mdi-24px">
|
||||
</b-icon>
|
||||
<span>Pins</span>
|
||||
</a>
|
||||
</li>
|
||||
<li :class="trueFalse2Class(inBoard)">
|
||||
<a @click="go2UserBoard">
|
||||
<b-icon
|
||||
type="is-dark"
|
||||
icon="folder-multiple-image"
|
||||
custom-size="mdi-24px">
|
||||
</b-icon>
|
||||
<span>Boards</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from './api';
|
||||
|
||||
export default {
|
||||
name: 'UserProfileCard.vue',
|
||||
props: {
|
||||
username: String,
|
||||
inBoard: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
inPins: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
location: window.location.host,
|
||||
avatarLoading: true,
|
||||
user: {
|
||||
avatar: '',
|
||||
username: '',
|
||||
},
|
||||
};
|
||||
},
|
||||
beforeMount() {
|
||||
this.initializeUser(this.username);
|
||||
},
|
||||
methods: {
|
||||
go2UserBoard() {
|
||||
this.$router.push(
|
||||
{ name: 'boards4user', params: { username: this.username } },
|
||||
);
|
||||
},
|
||||
go2UserPins() {
|
||||
this.$router.push(
|
||||
{ name: 'user', params: { user: this.username } },
|
||||
);
|
||||
},
|
||||
trueFalse2Class(boolValue) {
|
||||
if (boolValue) {
|
||||
return 'is-active';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
onAvatarLoaded() {
|
||||
this.avatarLoading = false;
|
||||
},
|
||||
initializeUser(username) {
|
||||
const self = this;
|
||||
api.User.fetchUserInfoByName(username).then(
|
||||
(user) => {
|
||||
if (user === null) {
|
||||
self.$router.push(
|
||||
{ name: 'PageNotFound' },
|
||||
);
|
||||
} else {
|
||||
self.user.avatar = `//gravatar.com/avatar/${user.gravatar}`;
|
||||
self.user.username = user.username;
|
||||
self.user.meta = user;
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
#user-home-container {
|
||||
margin-top: 2rem;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
box-shadow: 5px 5px 2px 1px rgba(0, 0, 255, .1);
|
||||
}
|
||||
@import '../components/utils/grid-layout';
|
||||
@include screen-grid-layout("#user-home-container");
|
||||
</style>
|
||||
@@ -238,6 +238,23 @@ const User = {
|
||||
},
|
||||
);
|
||||
},
|
||||
fetchUserInfoByName(username) {
|
||||
/* returns null if user not logged in */
|
||||
const url = `${API_PREFIX}profile/public-users/?username=${username}`;
|
||||
return new Promise(
|
||||
(resolve) => {
|
||||
axios.get(url).then(
|
||||
(resp) => {
|
||||
const users = resp.data;
|
||||
if (users.length === 0) {
|
||||
return resolve(null);
|
||||
}
|
||||
return resolve(users[0]);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
fetchUserInfo(force = false) {
|
||||
/* returns null if user not logged in */
|
||||
const self = this;
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<template>
|
||||
<div class="boards-for-user">
|
||||
<PHeader></PHeader>
|
||||
<UserProfileCard :in-board="true" :username="filters.boardUsername"></UserProfileCard>
|
||||
<Boards :filters="filters"></Boards>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PHeader from '../components/PHeader.vue';
|
||||
import UserProfileCard from '../components/UserProfileCard.vue';
|
||||
import Boards from '../components/Boards.vue';
|
||||
|
||||
export default {
|
||||
@@ -18,6 +20,7 @@ export default {
|
||||
},
|
||||
components: {
|
||||
PHeader,
|
||||
UserProfileCard,
|
||||
Boards,
|
||||
},
|
||||
created() {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<template>
|
||||
<div class="pins-for-user">
|
||||
<PHeader></PHeader>
|
||||
<UserProfileCard :in-pins="true" :username="filters.userFilter"></UserProfileCard>
|
||||
<Pins :pin-filters="filters"></Pins>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PHeader from '../components/PHeader.vue';
|
||||
import UserProfileCard from '../components/UserProfileCard.vue';
|
||||
import Pins from '../components/Pins.vue';
|
||||
|
||||
export default {
|
||||
@@ -18,6 +20,7 @@ export default {
|
||||
},
|
||||
components: {
|
||||
PHeader,
|
||||
UserProfileCard,
|
||||
Pins,
|
||||
},
|
||||
created() {
|
||||
|
||||
@@ -6,6 +6,21 @@ from rest_framework.exceptions import ValidationError
|
||||
from users.models import User
|
||||
|
||||
|
||||
class PublicUserSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = (
|
||||
'username',
|
||||
'gravatar',
|
||||
settings.DRF_URL_FIELD_NAME,
|
||||
)
|
||||
extra_kwargs = {
|
||||
settings.DRF_URL_FIELD_NAME: {
|
||||
"view_name": "public-users:user-detail",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class UserSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
|
||||
@@ -7,6 +7,7 @@ from django.contrib.auth.decorators import login_required
|
||||
from django.http import HttpResponseRedirect, HttpResponseBadRequest, HttpResponse
|
||||
from django.urls import reverse
|
||||
from django.utils.functional import lazy
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework import mixins, routers
|
||||
from rest_framework.permissions import BasePermission
|
||||
from rest_framework.renderers import JSONRenderer
|
||||
@@ -20,6 +21,21 @@ def reverse_lazy(name=None, *args):
|
||||
return lazy(reverse, str)(name, args=args)
|
||||
|
||||
|
||||
class PublicUserViewSet(
|
||||
mixins.RetrieveModelMixin,
|
||||
mixins.ListModelMixin,
|
||||
GenericViewSet,
|
||||
):
|
||||
serializer_class = UserSerializer
|
||||
filter_backends = (DjangoFilterBackend, )
|
||||
filter_fields = ("username", )
|
||||
pagination_class = None
|
||||
|
||||
def get_queryset(self):
|
||||
username = self.request.GET.get("username", "")
|
||||
return User.objects.filter(username=username)
|
||||
|
||||
|
||||
class UserViewSet(
|
||||
mixins.RetrieveModelMixin,
|
||||
mixins.ListModelMixin,
|
||||
@@ -87,3 +103,4 @@ def logout_user(request):
|
||||
|
||||
drf_router = routers.DefaultRouter()
|
||||
drf_router.register(r'users', UserViewSet, basename="user")
|
||||
drf_router.register(r'public-users', PublicUserViewSet, basename="public-user")
|
||||
|
||||
Reference in New Issue
Block a user