(null)
13 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.svelte-kit/tsconfig.json",
3 | "compilerOptions": {
4 | "allowJs": true,
5 | "checkJs": true,
6 | "esModuleInterop": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "resolveJsonModule": true,
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "strict": true
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | %sveltekit.head%
8 |
9 |
10 | %sveltekit.body%
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/services/user.ts:
--------------------------------------------------------------------------------
1 | export const getAccessToken = async ({ token }: { token: string}) => {
2 | const res = await fetch('/api/get-access-token', {
3 | headers: {
4 | jwt: token
5 | }
6 | })
7 |
8 | if (!res.ok) throw new Error('Error getting access token')
9 |
10 | const { accessToken } = await res.json()
11 | return accessToken
12 | }
--------------------------------------------------------------------------------
/src/app.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | // See https://kit.svelte.dev/docs/types#app
4 | // for information about these interfaces
5 | // and what to do when importing types
6 | declare namespace App {
7 | interface Locals {
8 | userid: string;
9 | }
10 |
11 | // interface Platform {}
12 |
13 | // interface Session {}
14 |
15 | // interface Stuff {}
16 | }
17 |
--------------------------------------------------------------------------------
/src/routes/index.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 | secret chat 🥷
9 |
10 |
11 |
12 |
13 | {#if $user} {/if}
14 |
--------------------------------------------------------------------------------
/src/components/LoggedUserHeader.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 | {#if $user}
7 | {$user.name}
8 |
9 |
10 |
11 | Cerrar sesión
12 |
13 | {/if}
14 |
--------------------------------------------------------------------------------
/src/routes/[room].svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 | {#if $activeConversation?.uniqueName}
9 |
10 |
11 | {$activeConversation.uniqueName}
12 |
13 |
14 |
15 |
16 | {/if}
17 |
--------------------------------------------------------------------------------
/src/components/LogoutIcon.svelte:
--------------------------------------------------------------------------------
1 |
8 |
14 |
20 |
21 |
--------------------------------------------------------------------------------
/svelte.config.js:
--------------------------------------------------------------------------------
1 | import adapter from '@sveltejs/adapter-auto';
2 | import preprocess from 'svelte-preprocess';
3 |
4 | /** @type {import('@sveltejs/kit').Config} */
5 | const config = {
6 | // Consult https://github.com/sveltejs/svelte-preprocess
7 | // for more information about preprocessors
8 | preprocess: preprocess({
9 | postcss: true
10 | }),
11 |
12 | kit: {
13 | adapter: adapter(),
14 |
15 | // Override http methods in the Todo forms
16 | methodOverride: {
17 | allowed: ['PATCH', 'DELETE']
18 | }
19 | }
20 | };
21 |
22 | export default config;
23 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parser: '@typescript-eslint/parser',
4 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
5 | plugins: ['svelte3', '@typescript-eslint'],
6 | ignorePatterns: ['*.cjs'],
7 | overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
8 | settings: {
9 | 'svelte3/typescript': () => require('typescript')
10 | },
11 | parserOptions: {
12 | sourceType: 'module',
13 | ecmaVersion: 2020
14 | },
15 | env: {
16 | browser: true,
17 | es2017: true,
18 | node: true
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/src/components/ConversationInput.svelte:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
23 |
24 |
--------------------------------------------------------------------------------
/src/components/Message.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
{body}
13 |
{author}
14 |
15 |
16 |
27 |
--------------------------------------------------------------------------------
/src/components/Conversation.svelte:
--------------------------------------------------------------------------------
1 |
22 |
23 |
24 | {#each messages as message}
25 |
26 | {/each}
27 |
28 |
--------------------------------------------------------------------------------
/src/services/chat.ts:
--------------------------------------------------------------------------------
1 | import { Client } from '@twilio/conversations'
2 |
3 | export const createOrJoinConversation = async (
4 | { room, accessToken } : { room: string, accessToken: string }
5 | ) => {
6 | const client = new Client(accessToken)
7 |
8 | return new Promise(resolve => {
9 | client.on('stateChanged', async state => {
10 | if (state === 'initialized') {
11 | let conversation
12 |
13 | try {
14 | conversation = await client.createConversation({ uniqueName: room })
15 |
16 | // await conversation?.add('ma')
17 | // await conversation?.add('mb')
18 |
19 | } catch (e) {
20 | console.error(e)
21 |
22 | try {
23 | conversation = await client.getConversationByUniqueName(room)
24 |
25 | // await conversation?.add('ma')
26 | // await conversation?.add('mb')
27 | } catch (e) {
28 | console.error(e)
29 | }
30 | }
31 |
32 | console.log(conversation)
33 |
34 | resolve(conversation)
35 | }
36 | })
37 | })
38 | }
--------------------------------------------------------------------------------
/src/components/UserHeader.svelte:
--------------------------------------------------------------------------------
1 |
25 |
26 |
27 | {#if $user}
28 |
29 | {:else if showAnonymousInput}
30 |
38 | {:else}
39 |
40 | Entrar como anónimo
41 |
42 | {/if}
43 |
44 |
--------------------------------------------------------------------------------
/src/components/Logo.svelte:
--------------------------------------------------------------------------------
1 |
4 |
25 | secretchat
26 |
27 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "secret-chat",
3 | "version": "0.0.1",
4 | "scripts": {
5 | "dev": "svelte-kit dev",
6 | "build": "svelte-kit build",
7 | "package": "svelte-kit package",
8 | "preview": "svelte-kit preview",
9 | "prepare": "svelte-kit sync",
10 | "test": "playwright test",
11 | "check": "svelte-check --tsconfig ./tsconfig.json",
12 | "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
13 | "lint": "prettier --check --plugin-search-dir=. . && eslint .",
14 | "format": "prettier --write --plugin-search-dir=. ."
15 | },
16 | "devDependencies": {
17 | "@playwright/test": "1.22.2",
18 | "@sveltejs/adapter-auto": "next",
19 | "@sveltejs/kit": "next",
20 | "@types/cookie": "0.5.1",
21 | "@typescript-eslint/eslint-plugin": "5.27.0",
22 | "@typescript-eslint/parser": "5.27.0",
23 | "autoprefixer": "^10.4.7",
24 | "eslint": "8.16.0",
25 | "eslint-config-prettier": "8.3.0",
26 | "eslint-plugin-svelte3": "4.0.0",
27 | "postcss": "^8.4.14",
28 | "prettier": "2.6.2",
29 | "prettier-plugin-svelte": "2.7.0",
30 | "svelte": "3.46.0",
31 | "svelte-check": "2.7.1",
32 | "svelte-preprocess": "^4.10.6",
33 | "tailwindcss": "^3.1.4",
34 | "tslib": "2.3.1",
35 | "typescript": "4.7.2"
36 | },
37 | "type": "module",
38 | "dependencies": {
39 | "@twilio/conversations": "^2.1.0",
40 | "twilio": "^3.78.0"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/components/RoomSelect.svelte:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
42 |
43 |
--------------------------------------------------------------------------------
/src/routes/api/get-access-token.ts:
--------------------------------------------------------------------------------
1 | import type { RequestHandler } from '@sveltejs/kit'
2 | import twilio from 'twilio'
3 |
4 | const TWILIO_ACCOUNT_SID = import.meta.env.VITE_PUBLIC_TWILIO_ACCOUNT_SID
5 | const TWILIO_API_KEY = import.meta.env.VITE_PUBLIC_TWILIO_API_KEY
6 | const TWILIO_API_SECRET = import.meta.env.VITE_PUBLIC_TWILIO_API_SECRET
7 | const SERVICE_SID = import.meta.env.VITE_PUBLIC_SERVICE_SID
8 |
9 | console.log({
10 | TWILIO_ACCOUNT_SID,
11 | TWILIO_API_KEY,
12 | TWILIO_API_SECRET,
13 | SERVICE_SID,
14 | })
15 |
16 | export const get: RequestHandler = async ({ request }) => {
17 | const jwt = request.headers.get('jwt')
18 | if (jwt == null) return { status: 401 }
19 |
20 | // This is temporal as we're allowing anonymous users
21 | const identity = jwt.startsWith('anonymous')
22 | ? jwt.split('_')[1]
23 | : null
24 |
25 | // Lo que haríamos de verdad
26 | // const user = await supabase.auth.api.getUser(jwt)
27 | // const identity = user.data.user_metadata.user_name
28 | // console.log({identity})
29 |
30 | if (identity == null) return { status: 401 }
31 |
32 | const { AccessToken } = twilio.jwt
33 | const { ChatGrant } = AccessToken
34 |
35 | const accessToken = new AccessToken(
36 | TWILIO_ACCOUNT_SID,
37 | TWILIO_API_KEY,
38 | TWILIO_API_SECRET, {
39 | identity
40 | })
41 |
42 | const conversationsGrant = new ChatGrant({
43 | serviceSid: SERVICE_SID
44 | })
45 |
46 | accessToken.addGrant(conversationsGrant)
47 |
48 | console.log(accessToken.toJwt())
49 |
50 | return {
51 | status: 200,
52 | body: {
53 | accessToken: accessToken.toJwt()
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
HACKATHON JULIO 2022 - TWILIO + CHAT SECRETO 🚀
5 |
6 | **🗓️ Fecha límite -> 20 de julio en Twitch.**
7 | 📹 Directo del 1 de julio con explicaciones -> https://www.twitch.tv/videos/1519558242
8 |
9 |
10 |
11 | ## - 🎁 PREMIOS para los 3 primeros:
12 |
13 | - [1] Keychron K3 Ultra-slim Wireless Mechanical Keyboard - Version 2 (Si no se puede enviar, se pagará lo que cueste el teclado con PayPal).
14 | - [2] 40€ en Redbubble.
15 | - [3] 20€ en Redbubble.
16 |
17 | ## - ⚠️ Requisitos:
18 |
19 | - **[1] Registrarse en Twilio -> https://www.twilio.com/referral/xdppiQ 🥰 **
20 | - [2] Crea un chat secreto dónde sólo la gente que tú quieras pueda hablar **usando los servicios de Twilio Conversations.**
21 | - [3] La app/web debe estar **desplegada** para que la podamos ver.
22 | - [4] **El repositorio de código debe ser abierto** y el **README** debe indicar que participa en la hackathon.
23 | - [5] :date: Enviar el repositorio + deploy en este canal, en este hilo: https://discord.com/channels/741237973663612969/992476955792375928/992478015164518520
24 |
25 | ## - 👀 Consideraciones:
26 | - Se tendrá en cuenta: ejecución del código (estructura...), look and feel (experiencia del usuario, diseño...), no puedes pillar una plantilla CSS de una aplicación que ya exista, que sea responsive y que funcione para móvil (mobile-first).
27 | - Se puede hacer con lo que quieras: Vanilla, Angular, React, Vue, Svelte, Nextjs, Nuxtjs, Remix...
28 | - Se puede hacer equipo :partying_face::partying_face:.
29 | - 💅 Te puedes inspirar en el diseño: https://dribbble.com/
30 |
31 | [🐙 Muchos más servicios disponibles en Twilio](https://www.twilio.com/referral/xdppiQ)
32 |
--------------------------------------------------------------------------------