Skip to content

Instantly share code, notes, and snippets.

@jsutterfield
Created January 23, 2021 15:13
Show Gist options
  • Select an option

  • Save jsutterfield/0afd6e93d79956e662f28be8c2a6beaa to your computer and use it in GitHub Desktop.

Select an option

Save jsutterfield/0afd6e93d79956e662f28be8c2a6beaa to your computer and use it in GitHub Desktop.

Revisions

  1. jsutterfield created this gist Jan 23, 2021.
    325 changes: 325 additions & 0 deletions SwipeNames.vue
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,325 @@
    <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>
    116 changes: 116 additions & 0 deletions webpack.common.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,116 @@
    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()
    ]
    };
    28 changes: 28 additions & 0 deletions webpack.dev.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,28 @@
    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"
    });
    7 changes: 7 additions & 0 deletions webpack.prod.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    const merge = require("webpack-merge");
    const common = require("./webpack.common.js");

    module.exports = merge(common, {
    devtool: "source-map",
    mode: "production"
    });