Feature: Add new public user-api and user-profile card in front-end

This commit is contained in:
winkidney
2020-07-23 23:56:24 +08:00
parent 978c686c66
commit dbca765b6a
6 changed files with 192 additions and 0 deletions

View 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>

View File

@@ -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) { fetchUserInfo(force = false) {
/* returns null if user not logged in */ /* returns null if user not logged in */
const self = this; const self = this;

View File

@@ -1,12 +1,14 @@
<template> <template>
<div class="boards-for-user"> <div class="boards-for-user">
<PHeader></PHeader> <PHeader></PHeader>
<UserProfileCard :in-board="true" :username="filters.boardUsername"></UserProfileCard>
<Boards :filters="filters"></Boards> <Boards :filters="filters"></Boards>
</div> </div>
</template> </template>
<script> <script>
import PHeader from '../components/PHeader.vue'; import PHeader from '../components/PHeader.vue';
import UserProfileCard from '../components/UserProfileCard.vue';
import Boards from '../components/Boards.vue'; import Boards from '../components/Boards.vue';
export default { export default {
@@ -18,6 +20,7 @@ export default {
}, },
components: { components: {
PHeader, PHeader,
UserProfileCard,
Boards, Boards,
}, },
created() { created() {

View File

@@ -1,12 +1,14 @@
<template> <template>
<div class="pins-for-user"> <div class="pins-for-user">
<PHeader></PHeader> <PHeader></PHeader>
<UserProfileCard :in-pins="true" :username="filters.userFilter"></UserProfileCard>
<Pins :pin-filters="filters"></Pins> <Pins :pin-filters="filters"></Pins>
</div> </div>
</template> </template>
<script> <script>
import PHeader from '../components/PHeader.vue'; import PHeader from '../components/PHeader.vue';
import UserProfileCard from '../components/UserProfileCard.vue';
import Pins from '../components/Pins.vue'; import Pins from '../components/Pins.vue';
export default { export default {
@@ -18,6 +20,7 @@ export default {
}, },
components: { components: {
PHeader, PHeader,
UserProfileCard,
Pins, Pins,
}, },
created() { created() {

View File

@@ -6,6 +6,21 @@ from rest_framework.exceptions import ValidationError
from users.models import User 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 UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta: class Meta:
model = User model = User

View File

@@ -7,6 +7,7 @@ from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect, HttpResponseBadRequest, HttpResponse from django.http import HttpResponseRedirect, HttpResponseBadRequest, HttpResponse
from django.urls import reverse from django.urls import reverse
from django.utils.functional import lazy from django.utils.functional import lazy
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import mixins, routers from rest_framework import mixins, routers
from rest_framework.permissions import BasePermission from rest_framework.permissions import BasePermission
from rest_framework.renderers import JSONRenderer from rest_framework.renderers import JSONRenderer
@@ -20,6 +21,21 @@ def reverse_lazy(name=None, *args):
return lazy(reverse, str)(name, args=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( class UserViewSet(
mixins.RetrieveModelMixin, mixins.RetrieveModelMixin,
mixins.ListModelMixin, mixins.ListModelMixin,
@@ -87,3 +103,4 @@ def logout_user(request):
drf_router = routers.DefaultRouter() drf_router = routers.DefaultRouter()
drf_router.register(r'users', UserViewSet, basename="user") drf_router.register(r'users', UserViewSet, basename="user")
drf_router.register(r'public-users', PublicUserViewSet, basename="public-user")