# Quasar Chat

quasar create firechat --branch next

# .vscode

{
  "vetur.validation.template": true,
  "vetur.format.enable": true,
  "eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"],

  "vetur.experimental.templateInterpolationService": true
}

# Boot

quasar new boot <name>

quasar.conf.js

boot: ["firebase"],
npm i @vueuse/firebase firebase
import { boot } from "quasar/wrappers";

console.log("firebase boot");

import firebase from "firebase/app";
import "firebase/auth";
import "firebase/firestore";

const firebaseConfig = {
  apikey...
};

// Initialize Firebase
firebase.initializeApp(firebaseConfig);

const db = firebase.firestore();
const auth = firebase.auth();
const marcaTiempo = firebase.firestore.FieldValue.serverTimestamp;

export { db, auth, marcaTiempo };


// "async" is optional;
// more info on params: https://v2.quasar.dev/quasar-cli/boot-files
export default boot(async (/* { app, router, ... } */) => {
  // something to do
});

export default ({ urlPath, redirect }) => {
  // ...
  const isAuthorized = // ...
  if (!isAuthorized && !urlPath.startsWith('/login')) {
    redirect({ path: '/login' })
    return
  }
  // ...
}

# History

quasar.conf.js

build: {
  vueRouterMode: "history",
}

# MainLayout

<template>
  <q-layout view="lHh Lpr lFf">
    <q-header elevated>
      <q-toolbar>
        <q-btn
          flat
          dense
          round
          icon="menu"
          aria-label="Menu"
          @click="toggleLeftDrawer"
        />

        <q-toolbar-title> Chat </q-toolbar-title>

        <div>botones</div>
      </q-toolbar>
    </q-header>

    <q-drawer v-model="leftDrawerOpen" show-if-above bordered class="bg-grey-1">
      <q-list>
        <q-item-label header class="text-grey-8">
          Essential Links
        </q-item-label>
      </q-list>
    </q-drawer>

    <q-page-container>
      <router-view />
    </q-page-container>
  </q-layout>
</template>

<script>
import { ref } from "vue";

export default {
  name: "MainLayout",

  components: {},

  setup() {
    const leftDrawerOpen = ref(false);

    const toggleLeftDrawer = () => {
      leftDrawerOpen.value = !leftDrawerOpen.value;
    };

    return {
      leftDrawerOpen,
      toggleLeftDrawer,
    };
  },
};
</script>

# VistaAcceso

<template>
  <div class="q-px-xl">
    <h5>{{ acceder ? "Login" : "Registro" }}</h5>
    <q-form class="q-gutter-md" @submit.prevent="registro">
      <q-input label="Email" v-model="email" />
      <q-input label="Password" v-model="password" />
      <q-btn color="primary" type="submit">{{
        acceder ? "Login" : "Registro"
      }}</q-btn>
      <q-btn color="primary" outline @click="acceder = true" v-if="!acceder">
        ¿Ya tienes cuenta?
      </q-btn>
      <q-btn color="negative" outline @click="acceder = false" v-else>
        ¿No tienes cuenta?
      </q-btn>
    </q-form>
  </div>
</template>

<script>
import { ref } from "vue";
import { auth, db } from "boot/firebase";
export default {
  setup() {
    const email = ref("prueba@prueba.com");
    const password = ref("123123");

    const acceder = ref(true);

    const registro = async () => {
      if (!email.value.trim() || !password.value.trim()) {
        console.log("campos vacios");
        return;
      }
      try {
        if (acceder.value) {
          const userCredential = await auth.signInWithEmailAndPassword(
            email.value,
            password.value
          );
          const userDB = userCredential.user;
          await db.collection("users").doc(userDB.uid).update({
            estado: true,
          });
        } else {
          const userCredential = await auth.createUserWithEmailAndPassword(
            email.value,
            password.value
          );
          const userDB = userCredential.user;
          await db.collection("users").doc(userDB.uid).set({
            correo: userDB.email,
            uid: userDB.uid,
            estado: true,
          });
        }
        email.value = "";
        password.value = "";
      } catch (error) {
        console.log(error);
      }
    };

    return { email, password, registro, acceder };
  },
};
</script>

# pages/Index.vue

<template>
  <q-page padding>
    <VistaAcceso v-if="!isAuthenticated" />
  </q-page>
</template>

<script>
import VistaAcceso from "../components/VistaAcceso";

import { useAuth } from "@vueuse/firebase";
export default {
  components: {
    VistaAcceso,
  },
  setup() {
    const { user, isAuthenticated } = useAuth();

    return {
      user,
      isAuthenticated,
    };
  },
};
</script>

# Layout

<template>
  <q-layout view="lHh Lpr lFf">
    <q-header bordered>
      <q-toolbar>
        <q-toolbar-title :class="isAuthenticated ? '' : 'text-center'">
          {{ isAuthenticated ? user.email : "Chat" }}
        </q-toolbar-title>

        <div v-if="isAuthenticated">
          <q-btn color="negative" label="Salir" @click="salir" />
        </div>
      </q-toolbar>
    </q-header>

    <q-page-container>
      <router-view />
    </q-page-container>
  </q-layout>
</template>

<script>
import { useAuth } from "@vueuse/firebase";
import { db, auth } from "boot/firebase";

export default {
  name: "MainLayout",

  components: {},

  setup() {
    const { user, isAuthenticated } = useAuth();

    const salir = async () => {
      try {
        await db.collection("users").doc(user.value.uid).update({
          estado: false,
        });
        await auth.signOut();
      } catch (error) {
        console.log(error);
      }
    };

    return {
      user,
      isAuthenticated,
      salir,
    };
  },
};
</script>

# VistaUsuariosActivos.vue

<template>
  <q-page-sticky position="top" expand>
    <q-tabs
      v-model="tab"
      inline-label
      outside-arrows
      mobile-arrows
      class="bg-primary text-white shadow-2 full-width"
    >
      <q-tab name="mails" icon="account_circle" label="Mails" />
      <q-tab name="alarms" icon="account_circle" label="Alarms" />
      <q-tab name="movies" icon="account_circle" label="Movies" />
      <q-tab name="photos" icon="account_circle" label="Photos" />
      <q-tab name="videos" icon="account_circle" label="Videos" />
      <q-tab name="addressbook" icon="account_circle" label="Address Book" />
    </q-tabs>
  </q-page-sticky>
</template>

<script>
import { ref } from "vue";

export default {
  setup() {
    return {
      tab: ref("mails"),
    };
  },
};
</script>

Index.vue

<template>
  <q-page padding>
    <VistaAcceso v-if="!isAuthenticated" />
    <div v-else>
      <VistaUsuariosActivos />
      <VistaChat />
    </div>
  </q-page>
</template>

Mostrar usuario activos

<template>
  <q-page-sticky position="top" expand class="tabs-zindex">
    <q-tabs
      v-model="selecUserChat"
      inline-label
      outside-arrows
      mobile-arrows
      class="bg-primary text-white shadow-2 full-width"
    >
      <q-tab
        v-for="user in arraySinUser"
        :key="user.uid"
        icon="account_circle"
        :label="user.correo"
        :name="user.uid"
        :class="user.estado ? 'text-white' : 'text-grey'"
      />
    </q-tabs>
  </q-page-sticky>
</template>

<script>
import { ref, inject, computed } from "vue";
import { db } from "boot/firebase";
import { useAuth } from "@vueuse/firebase";

export default {
  setup() {
    const users = ref([]);
    const selecUserChat = inject("selecUserChat");
    const { user } = useAuth();

    db.collection("users").onSnapshot((snapshot) => {
      snapshot.docChanges().forEach((change) => {
        if (change.type === "added") {
          users.value = [...users.value, change.doc.data()];
          users.value = users.value.sort((a, b) => b.estado - a.estado);
        }
        if (change.type === "modified") {
          users.value = users.value.map((user) =>
            user.uid === change.doc.data().uid
              ? { ...user, estado: change.doc.data().estado }
              : user
          );

          users.value = users.value.sort((a, b) => b.estado - a.estado);
        }
        if (change.type === "removed") {
          users.value = [...users.value, change.doc.data()];
          users.value = users.value.sort((a, b) => b.estado - a.estado);
        }
      });
    });

    const arraySinUser = computed(() => {
      return users.value.filter((item) => item.uid !== user.value.uid);
    });
    return {
      selecUserChat,
      users,
      arraySinUser,
    };
  },
};
</script>

<style scoped>
.tabs-zindex {
  z-index: 2000;
}
</style>

index.vue

<template>
  <q-page padding>
    <VistaAcceso v-if="!isAuthenticated" />
    <div v-else>
      <VistaUsuariosActivos />
      <VistaChat />
    </div>
  </q-page>
</template>

<script>
import VistaAcceso from "../components/VistaAcceso";
import VistaChat from "../components/VistaChat";
import VistaUsuariosActivos from "../components/VistaUsuariosActivos";

import { useAuth } from "@vueuse/firebase";
import { ref, provide, watchEffect } from "vue";

export default {
  components: {
    VistaAcceso,
    VistaChat,
    VistaUsuariosActivos,
  },
  setup() {
    const { user, isAuthenticated } = useAuth();

    const selecUserChat = ref("");
    provide("selecUserChat", selecUserChat);

    watchEffect(() => {
      console.log("selecUserChat", selecUserChat.value);
    });

    return {
      user,
      isAuthenticated,
    };
  },
};
</script>

# VistaChat.vue

<template>
  <div ref="RefChat" v-if="selecUserChat !== ''" class="q-mt-xl">
    <div class="q-pa-md row justify-center">
      <div style="width: 100%; max-width: 600px">
        <q-chat-message
          v-for="chat in chats"
          :key="chat.id"
          :name="chat.user"
          :text="[chat.texto]"
          :sent="chat.uid === user.uid"
        />
      </div>
    </div>

    <q-footer>
      <q-form @submit.prevent="enviarMensaje">
        <q-toolbar class="bg-primary text-white row">
          <q-btn round flat icon="insert_emoticon" class="q-mr-sm" />
          <q-input
            v-model="message"
            class="col-grow q-mr-sm"
            bg-color="white"
            placeholder="Type a message"
            dense
            outlined
            rounded
            autofocus
            ref="inputFocus"
          />
          <q-btn round flat icon="send" type="submit" />
        </q-toolbar>
      </q-form>
    </q-footer>
  </div>
  <div v-else class="flex flex-center q-mt-xl q-pt-xl">
    <h6>Selecciona un usuario para el chat</h6>
  </div>
</template>

<script>
import { ref, inject, watch } from "vue";
import { useAuth } from "@vueuse/firebase";
import { db, marcaTiempo } from "boot/firebase";

export default {
  setup() {
    const message = ref("");
    const inputFocus = ref(null);
    const { user } = useAuth();
    const selecUserChat = inject("selecUserChat");
    const chats = ref([]);
    const RefChat = ref(null);

    let unsubscribe;

    const obtenerData = (idParams) => {
      chats.value = [];
      unsubscribe = db
        .collection("chat")
        .doc(user.value.uid)
        .collection(idParams)
        .orderBy("fecha")
        .onSnapshot((snapshot) => {
          snapshot.docChanges().forEach((change) => {
            if (change.type === "added") {
              chats.value.push({ ...change.doc.data(), id: change.doc.id });
            }
            if (RefChat.value !== null) {
              setTimeout(() => {
                window.scrollTo(0, RefChat.value.scrollHeight);
              }, 60);
            }
          });
        });
    };

    watch(
      () => selecUserChat.value,
      (newId) => {
        if (unsubscribe) {
          unsubscribe();
          if (newId) {
            obtenerData(newId);
          }
        } else {
          obtenerData(newId);
        }
        console.log("watch");
        console.log("newId", newId);
      }
    );

    const enviarMensaje = async () => {
      try {
        const objetoMensaje = {
          user: user.value.email,
          texto: message.value,
          fecha: marcaTiempo(),
          uid: user.value.uid,
        };
        await db
          .collection("chat")
          .doc(user.value.uid)
          .collection(selecUserChat.value)
          .add(objetoMensaje);
        await db
          .collection("chat")
          .doc(selecUserChat.value)
          .collection(user.value.uid)
          .add(objetoMensaje);
        message.value = "";
        inputFocus.value.focus();
      } catch (error) {
        console.log(error);
      }
    };

    return {
      enviarMensaje,
      message,
      inputFocus,
      chats,
      RefChat,
      user,
      selecUserChat,
    };
  },
};
</script>