Add support for Supporter badges

This commit is contained in:
Will Webberley 2022-12-16 14:32:14 +00:00
parent 2adcbaa7c2
commit b0d32b6578
9 changed files with 65 additions and 29 deletions

View File

@ -71,7 +71,7 @@ def create(user, data):
def get(user, username, path):
db = database.get_db()
owner = db.users.find_one({'username': username}, {'_id': 1, 'username': 1, 'avatar': 1})
owner = db.users.find_one({'username': username}, {'_id': 1, 'username': 1, 'avatar': 1, 'isSilverSupporter': 1})
if not owner: raise util.errors.NotFound('User not found')
project = db.projects.find_one({'user': owner['_id'], 'path': path})
if not project: raise util.errors.NotFound('Project not found')

View File

@ -8,7 +8,7 @@ def all(user, params):
expression = re.compile(params['query'], re.IGNORECASE)
db = database.get_db()
users = list(db.users.find({'username': expression}, {'username': 1, 'avatar': 1}).limit(10).sort('username', pymongo.ASCENDING))
users = list(db.users.find({'username': expression}, {'username': 1, 'avatar': 1, 'isSilverSupporter': 1}).limit(10).sort('username', pymongo.ASCENDING))
for u in users:
if 'avatar' in u:
u['avatarUrl'] = uploads.get_presigned_url('users/{0}/{1}'.format(u['_id'], u['avatar']))
@ -36,7 +36,7 @@ def users(user, params):
if not params or 'username' not in params: raise util.errors.BadRequest('Username parameter needed')
expression = re.compile(params['username'], re.IGNORECASE)
db = database.get_db()
users = list(db.users.find({'username': expression}, {'username': 1, 'avatar': 1}).limit(5).sort('username', pymongo.ASCENDING))
users = list(db.users.find({'username': expression}, {'username': 1, 'avatar': 1, 'isSilverSupporter': 1}).limit(5).sort('username', pymongo.ASCENDING))
for u in users:
if 'avatar' in u:
u['avatarUrl'] = uploads.get_presigned_url('users/{0}/{1}'.format(u['_id'], u['avatar']))
@ -60,7 +60,7 @@ def discover(user):
if len(projects) >= count: break
interest_fields = ['bio', 'avatar', 'website', 'facebook', 'twitter', 'instagram', 'location']
all_users = list(db.users.find({'_id': {'$ne': user['_id']}, '$or': list(map(lambda f: {f: {'$exists': True}}, interest_fields))}, {'username': 1, 'avatar': 1}))
all_users = list(db.users.find({'_id': {'$ne': user['_id']}, '$or': list(map(lambda f: {f: {'$exists': True}}, interest_fields))}, {'username': 1, 'avatar': 1, 'isSilverSupporter': 1}))
random.shuffle(all_users)
for u in all_users:
if 'avatar' in u:

View File

@ -15,11 +15,12 @@ def me(user):
'groups': user.get('groups', []),
'subscriptions': user.get('subscriptions'),
'finishedTours': user.get('completedTours', []) + user.get('skippedTours', []),
'isSilverSupporter': user.get('isSilverSupporter'),
}
def get(user, username):
db = database.get_db()
fetch_user = db.users.find_one({'username': username}, {'username': 1, 'createdAt': 1, 'avatar': 1, 'avatarBlurHash': 1, 'bio': 1, 'location': 1, 'website': 1, 'twitter': 1, 'facebook': 1, 'linkedIn': 1, 'instagram': 1})
fetch_user = db.users.find_one({'username': username}, {'username': 1, 'createdAt': 1, 'avatar': 1, 'avatarBlurHash': 1, 'bio': 1, 'location': 1, 'website': 1, 'twitter': 1, 'facebook': 1, 'linkedIn': 1, 'instagram': 1, 'isSilverSupporter': 1})
if not fetch_user:
raise util.errors.NotFound('User not found')
project_query = {'user': fetch_user['_id']}

Binary file not shown.

Binary file not shown.

View File

@ -9,6 +9,7 @@ import utils from '../../utils/utils.js';
import logoLight from '../../images/logo/light.png';
import UserChip from './UserChip';
import SupporterBadge from './SupporterBadge';
const StyledNavBar = styled.div`
height:60px;
@ -185,11 +186,7 @@ function NavBar() {
</span>
<Dropdown direction="left" pointing="top right" icon={null} style={{marginLeft: 10}}
trigger={
<div style={{
display: 'inline-block', width: 33, height: 33, borderRadius: '50%', backgroundSize: 'cover', backgroundPosition: 'center center', backgroundImage: `url(${utils.avatarUrl(user)})`, verticalAlign: 'middle', boxShadow: '0px 5px 10px rgba(0,0,0,0.1)', border: '2px solid white'
}} />
}
trigger={<UserChip user={user} withoutLink avatarOnly />}
>
<Dropdown.Menu style={{ minWidth: '200px', paddingTop: 10 }}>
{user &&
@ -201,6 +198,7 @@ function NavBar() {
<span>{user.username}</span>
</Dropdown.Header>
}
{user?.isSilverSupporter && <Dropdown.Header><SupporterBadge type='silver' /></Dropdown.Header>}
<Dropdown.Divider />
<Link to="/" className="item">Projects</Link>
{user &&<Link to={`/${user.username}`} className="item">Profile</Link>}

View File

@ -0,0 +1,21 @@
import React from 'react';
import { Icon, Popup, Label, Button } from 'semantic-ui-react';
export default function SupporterBadge({ type, compact }) {
if (type === 'silver')
return (
compact ?
<Popup basic
trigger={<Label size='mini' circular color='black' style={{borderRadius: '50%', width: 20, height: 20}} icon='trophy' />
}
>
<Popup.Content>Silver Supporter</Popup.Content>
</Popup>
:
<Label color='black' icon>
<Icon name='trophy' />
<Label.Detail>Silver Supporter</Label.Detail>
</Label>
);
return null;
}

View File

@ -3,28 +3,40 @@ import { Link } from 'react-router-dom';
import styled from 'styled-components';
import utils from '../../utils/utils.js';
const Avatar = styled.div`
display: inline-block;
width: ${props => props.compact ? 15 : 30}px;
height: ${props => props.compact ? 15 : 30}px;
border-radius: 50%;
background-size: cover;
background-position: center center;
background-image: url(${props => utils.cropUrl(utils.avatarUrl(props.user), 50, 50)});
vertical-align: middle;
margin-right: ${props => props.compact ? 4 : 8}px;
`;
const Username = styled.span`
font-size: ${props => props.compact ? 10 : 12}px;
`;
import SupporterBadge from './SupporterBadge';
function UserChip({ user, compact, meta, style }) {
const Avatar = styled.div`
display: inline-block;
position: relative;
width: ${props => props.compact ? 15 : 30}px;
height: ${props => props.compact ? 15 : 30}px;
border-radius: 50%;
background-size: cover;
background-position: center center;
background-image: url(${props => utils.cropUrl(utils.avatarUrl(props.user), 50, 50)});
vertical-align: middle;
margin-right: ${props => props.avatarOnly ? 0 : (props.compact ? 4 : 8)}px;
`;
const Badge = styled.div`
position: absolute;
top: -8px;
left: -8px;
`;
const Username = styled.span`
font-size: ${props => props.compact ? 10 : 12}px;
`;
function UserChip({ user, compact, meta, withoutLink, avatarOnly, style }) {
if (!user) return null;
return (
<Link to={`/${user.username}`} style={style || {}}>
<Avatar compact={compact} user={user} />
<Username compact={compact}>{user.username}</Username>
{meta && <span style={{color:'rgb(180,180,180)', fontSize: 10, marginLeft: 10}}>{meta}</span>}
<Link to={withoutLink ? '#' : `/${user.username}`} style={style || {}}>
<Avatar compact={compact} user={user} avatarOnly={avatarOnly}>
{user.isSilverSupporter && <Badge><SupporterBadge compact type='silver' /></Badge>}
</Avatar>
{!avatarOnly && <>
<Username compact={compact}>{user.username}</Username>
{meta && <span style={{color:'rgb(180,180,180)', fontSize: 10, marginLeft: 10}}>{meta}</span>}
</>}
</Link>
);
}

View File

@ -9,6 +9,7 @@ import actions from '../../../actions';
import api from '../../../api';
import BlurrableImage from '../../includes/BlurrableImage';
import SupporterBadge from '../../includes/SupporterBadge';
function Profile() {
const [loading, setLoading] = useState(false);
@ -69,6 +70,9 @@ function Profile() {
Joined {moment(profileUser.createdAt).fromNow()}
</span>
</Card.Meta>
{profileUser.isSilverSupporter &&
<div style={{marginTop: 10}}><SupporterBadge type='silver' /></div>
}
</Card.Content>
{profileUser.location
&& (