Skip to content

Instantly share code, notes, and snippets.

@jsutterfield
Created January 23, 2021 15:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jsutterfield/0afd6e93d79956e662f28be8c2a6beaa to your computer and use it in GitHub Desktop.
Save jsutterfield/0afd6e93d79956e662f28be8c2a6beaa to your computer and use it in GitHub Desktop.
<template>
<div id="app" class="tinder-app">
<MatchModal
v-if="showMatchModal"
:gender="latestMatchGender"
:name="latestMatchName"
v-on:click.native="showMatchModal = false"
/>
<ConnectAccountsModal
v-if="showConnectModal"
@close-connect-modal="closeConnectModal"
/>
<OutOfNamesModal
v-if="showOutOfNamesModal"
:namesCount="countOfNamesWithCurrentFilters"
/>
<div class="tinder-container">
<Tinder
ref="tinder"
:queue.sync="queue"
:offset-y="0.75"
offsetUnit="rem"
@submit="onSubmit"
key-name="id"
>
<template slot-scope="nameObject">
<div
class="card"
:class="[
{
boycard: nameObject.data.gender === 'm',
girlcard: nameObject.data.gender === 'f'
}
]"
>
<div :style="{ marginBottom: lastName ? '1rem' : '2rem' }">
<h3>{{ nameObject.data.name }}</h3>
<h4 v-if="lastName">{{ lastName }}</h4>
</div>
<img svg-inline class="card__feet" src="../../svg/feet.svg" />
</div>
</template>
<div class="like-pointer" slot="like">
<p>Like</p>
</div>
<div class="nope-pointer" slot="nope">
<p>Nope</p>
</div>
</Tinder>
<div class="tinder-buttons">
<div class="tinder-button" @click="decide('nope')">
<img svg-inline class="tinder-button__x" src="../../svg/x.svg" />
</div>
<div
class="tinder-button tinder-button--small"
@click="decide('rewind')"
>
<img
svg-inline
class="tinder-button__go_back"
src="../../svg/go_back.svg"
/>
</div>
<div class="tinder-button" @click="decide('like')">
<img
svg-inline
class="tinder-button__heart"
src="../../svg/heart.svg"
/>
</div>
</div>
</div>
</div>
</template>
<script>
import Tinder from "vue-tinder";
import MatchModal from "./MatchModal.vue";
import ConnectAccountsModal from "./ConnectAccountsModal.vue";
import OutOfNamesModal from "./OutOfNamesModal.vue";
import axios from "axios";
import Cookies from "js-cookie";
export default {
name: "App",
components: { Tinder, MatchModal, ConnectAccountsModal, OutOfNamesModal },
data() {
const MyAccount = window.SwipeNames;
return {
queue: [],
history: [],
lastName: null,
latestMatchName: null,
latestMatchGender: null,
showMatchModal: false,
showConnectModal: false,
showOutOfNamesModal: false,
countOfNamesWithCurrentFilters: 0,
shouldShowConnectModal: MyAccount.shouldShowConnectModal
};
},
created() {
this.fetchMoreNames();
},
methods: {
onSubmit({ type, item, key }) {
this.history.push(item);
// TODO clean this up a bit
const like_url = `/api/names/${key}/like`;
const dislike_url = `/api/names/${key}/dislike`;
const url_to_use = type === "like" ? like_url : dislike_url;
const csrf_token = Cookies.get("csrftoken");
axios
.post(url_to_use, null, {
headers: { "X-CSRFToken": csrf_token }
})
.then(response => {
if (this.queue.length === 0) {
this.fetchMoreNames();
}
const data = response.data;
if (data.member_of_group_likes_name) {
const liked_name = response.data.name;
this.latestMatchName = liked_name.text;
this.latestMatchGender = liked_name.gender;
this.showMatchModal = true;
}
if (this.history.length === 20 && this.shouldShowConnectModal) {
this.showConnectModal = true;
axios.post("/api/users/set-has-seen-connect-modal", null, {
headers: { "X-CSRFToken": csrf_token }
});
}
});
},
fetchMoreNames() {
const vm = this;
axios.get("/api/names").then(response => {
// TODO add error handling
const data = response.data;
vm.queue = vm.queue.concat(data.names);
vm.lastName = data.last_name;
if (vm.queue.length === 0) {
axios.get("/api/users/count-of-names").then(response => {
vm.countOfNamesWithCurrentFilters = response.data.count;
vm.showOutOfNamesModal = true;
});
}
});
},
async decide(choice) {
if (choice === "rewind") {
if (this.history.length) {
this.$refs.tinder.rewind([this.history.pop()]);
}
} else {
this.$refs.tinder.decide(choice);
}
},
closeConnectModal() {
this.showConnectModal = false;
this.shouldShowConnectModal = false;
}
}
};
</script>
<style lang="scss">
@import "../../sass/abstract/variables";
@import "../../sass/abstract/mixins";
.tinder-app {
text-align: center;
}
.tinder-container {
display: inline-flex;
flex-direction: column;
align-items: center;
margin-top: 20vh;
margin-bottom: 5rem;
@include respond($phone-landscape) {
// margin-top: 10rem;
}
@include respond($phone) {
margin-top: 22vh;
}
}
.vue-tinder {
width: 30rem;
height: 30rem;
}
.card {
width: 100%;
height: 100%;
text-align: center;
padding-top: 2rem;
&__feet {
height: 12rem;
width: 12rem;
margin-top: 1rem;
}
h3 {
font-size: 5rem;
font-family: $oswald-font-family;
letter-spacing: 0.3rem;
margin-bottom: -1rem;
}
h4 {
font-size: 2.5rem;
}
}
.boycard {
background-color: $color-boy-card;
.card__feet {
fill: $color-boy-feet;
}
}
.girlcard {
background-color: $color-girl-card;
.card__feet {
fill: $color-girl-feet;
}
}
.tinder-buttons {
display: flex;
margin-top: 4rem;
align-items: center;
}
.tinder-button {
background-color: $color-white;
border-radius: 500%;
margin: 0 2rem;
height: 7rem;
width: 7rem;
position: relative;
cursor: pointer;
&:active {
transform: translateY(0.2rem);
box-shadow: 0 0.5rem 1rem rgba($color-black, 0.2);
}
svg {
position: absolute;
top: 50%;
left: 50%;
transform: translateY(-50%) translateX(-50%);
height: 4rem;
width: 4rem;
&:focus {
outline: none;
}
}
&__x {
fill: $color-black;
}
&__heart {
fill: $color-primary-orange;
}
&__go_back {
fill: #00a79d;
}
&--small {
height: 4rem;
width: 4rem;
svg {
height: 2.5rem;
width: 2.5rem;
}
}
}
.nope-pointer,
.like-pointer {
position: absolute;
z-index: 1;
top: 2rem;
font-family: $oswald-font-family;
padding: 0 1rem;
border: 2px solid $color-white;
border-radius: 2px;
text-transform: uppercase;
font-size: 3rem;
line-height: 1.2;
p {
padding: 0;
margin: 0;
}
}
.like-pointer {
left: 2rem;
transform: rotate(-20deg);
}
.nope-pointer {
right: 2rem;
transform: rotate(20deg);
}
</style>
const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const BundleTracker = require("webpack-bundle-tracker");
const { VueLoaderPlugin } = require("vue-loader");
module.exports = {
entry: {
contact: "./app/static/sass/pages/contact.scss",
home: "./app/static/sass/pages/home.scss",
name_preferences_styles: "./app/static/sass/pages/name_preferences.scss",
name_preferences_js: [
"./app/static/javascript/name_preferences.js",
"./app/static/javascript/theme_buttons.js"
],
swipe_names_styles: "./app/static/sass/pages/swipe_names.scss",
swipe_names_app: [
"./app/static/javascript/swipe_names.js",
"./app/static/javascript/theme_buttons.js"
],
names_list_styles: "./app/static/sass/pages/names_list.scss",
names_list_js: "./app/static/javascript/names_list.js",
add_name_modal: "./app/static/javascript/add_name_modal.js",
sentry: "./app/static/javascript/sentry_bootstrap.js",
login: "./app/static/sass/pages/login.scss",
create_account: "./app/static/sass/pages/create_account.scss",
connect: "./app/static/sass/pages/connect.scss",
connections: "./app/static/sass/pages/connections.scss",
confirm_connection: "./app/static/sass/pages/confirm_connection.scss",
connect_error: "./app/static/sass/pages/connect_error.scss",
four_oh_four: "./app/static/sass/pages/404.scss",
password_reset: "./app/static/sass/pages/password_reset.scss",
success: "./app/static/sass/pages/success.scss",
// prettier-ignore
password_reset_confirm: "./app/static/sass/pages/password_reset_confirm.scss",
// prettier-ignore
accounts_form_button_enable_js: "./accounts/static/javascript/accounts_form_button_enable.js",
my_account_js: "./app/static/javascript/my_account.js",
my_account_styles: "./app/static/sass/pages/my_account.scss",
unsubscribe: "./app/static/sass/pages/unsubscribe.scss"
},
output: {
path: path.resolve("./app/static/bundles/"),
filename: "[name]-[hash].js"
},
module: {
rules: [
{
test: /\.js$/,
loader: "babel-loader"
},
{
test: /\.(woff|ttf)/,
use: [
{
loader: "file-loader",
options: {
name: "[name].[ext]",
outputPath: "fonts/"
}
}
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
{
loader: "file-loader",
options: {
esModule: false,
publicPath: "/static/bundles/"
}
}
]
},
{
test: /\.(sa|sc|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: "css-loader"
},
// {
// // Then we apply postCSS fixes like autoprefixer and minifying
// TODO add this to the production build
// loader: "postcss-loader"
// },
{
loader: "sass-loader",
options: {
implementation: require("sass")
}
}
]
},
{
test: /\.vue$/,
use: [
{
loader: "vue-loader"
},
{
loader: "vue-svg-inline-loader"
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({ filename: "[name]-[hash].css" }),
new BundleTracker({ filename: "./webpack-stats.json" }),
new VueLoaderPlugin()
]
};
const merge = require("webpack-merge");
const common = require("./webpack.common.js");
module.exports = merge(common, {
// devtool: "source-map",
// This stackoverflow was helpful for getting this setup
// https://stackoverflow.com/questions/57394214/django-webpack-how-to-serve-generated-webpack-bundles-with-webpack-dev-server
devServer: {
open: true,
host: "0.0.0.0",
port: 9090,
disableHostCheck: true,
// Note: in order to make inline reloading work you _must_ output a js bundle from webpack. If you try to use
// it only for css, and only output the css file, then webpack dev server can't add the bit of js it uses to force
// the browser to reload.
inline: true,
publicPath: "/static/bundles/",
proxy: {
"!/static/bundles/**": {
target: "http://localhost:8000", // points to django dev server
changeOrigin: true
}
}
},
mode: "development"
});
const merge = require("webpack-merge");
const common = require("./webpack.common.js");
module.exports = merge(common, {
devtool: "source-map",
mode: "production"
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment