├── .npmrc ├── bun.lockb ├── src ├── lib │ ├── index.ts │ ├── components │ │ └── ui │ │ │ ├── label │ │ │ ├── index.ts │ │ │ └── label.svelte │ │ │ ├── dialog │ │ │ ├── dialog-portal.svelte │ │ │ ├── dialog-header.svelte │ │ │ ├── dialog-footer.svelte │ │ │ ├── dialog-title.svelte │ │ │ ├── dialog-description.svelte │ │ │ ├── dialog-overlay.svelte │ │ │ ├── index.ts │ │ │ └── dialog-content.svelte │ │ │ ├── select │ │ │ ├── select-separator.svelte │ │ │ ├── select-label.svelte │ │ │ ├── index.ts │ │ │ ├── select-trigger.svelte │ │ │ ├── select-content.svelte │ │ │ └── select-item.svelte │ │ │ ├── button │ │ │ ├── button.svelte │ │ │ └── index.ts │ │ │ └── input │ │ │ ├── index.ts │ │ │ └── input.svelte │ ├── db │ │ └── db.ts │ ├── constants.ts │ └── utils.ts ├── routes │ ├── +layout.svelte │ ├── +layout.server.ts │ ├── +page.server.ts │ ├── api │ │ ├── get-communities │ │ │ └── +server.ts │ │ ├── get-community │ │ │ └── +server.ts │ │ └── create-community │ │ │ └── +server.ts │ ├── [username] │ │ ├── +page.server.ts │ │ └── +page.svelte │ └── +page.svelte ├── app.d.ts ├── store │ └── store.ts ├── app.css ├── components │ ├── socials.svelte │ ├── signInButton.svelte │ ├── header.svelte │ ├── footer.svelte │ ├── eachCommunity.svelte │ ├── radioComponent.svelte │ └── addCommunity.svelte ├── models │ └── community.model.ts ├── hooks.server.ts └── app.html ├── static ├── favicon.png ├── favicon0.png └── Screenshot.jpg ├── .prettierignore ├── postcss.config.js ├── .dockerignore ├── vite.config.ts ├── .gitignore ├── .eslintignore ├── .prettierrc ├── .env.example ├── components.json ├── .vscode └── extensions.json ├── Dockerfile ├── .eslintrc.cjs ├── svelte.config.js ├── tsconfig.json ├── LICENSE ├── package.json ├── tailwind.config.js └── README.md /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dagmawibabi/TelegramCommunityGallery/HEAD/bun.lockb -------------------------------------------------------------------------------- /src/lib/index.ts: -------------------------------------------------------------------------------- 1 | // place files you want to import through the `$lib` alias in this folder. 2 | -------------------------------------------------------------------------------- /src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dagmawibabi/TelegramCommunityGallery/HEAD/static/favicon.png -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore files for PNPM, NPM and YARN 2 | pnpm-lock.yaml 3 | package-lock.json 4 | yarn.lock 5 | -------------------------------------------------------------------------------- /static/favicon0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dagmawibabi/TelegramCommunityGallery/HEAD/static/favicon0.png -------------------------------------------------------------------------------- /static/Screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dagmawibabi/TelegramCommunityGallery/HEAD/static/Screenshot.jpg -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .svelte-kit 3 | build 4 | node_modules 5 | .dockerignore 6 | .gitignore 7 | Dockerfile 8 | README.md 9 | -------------------------------------------------------------------------------- /src/lib/components/ui/label/index.ts: -------------------------------------------------------------------------------- 1 | import Root from "./label.svelte"; 2 | 3 | export { 4 | Root, 5 | // 6 | Root as Label, 7 | }; 8 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import { defineConfig } from 'vite'; 3 | 4 | export default defineConfig({ 5 | plugins: [sveltekit()] 6 | }); 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | vite.config.js.timestamp-* 10 | vite.config.ts.timestamp-* 11 | .history -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte"], 7 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 8 | } 9 | -------------------------------------------------------------------------------- /src/routes/+layout.server.ts: -------------------------------------------------------------------------------- 1 | import type { LayoutServerLoad } from "./$types" 2 | 3 | // -> load serverside any data we need. 4 | 5 | export const load: LayoutServerLoad = async (event) =>{ 6 | return { 7 | session: await event.locals.auth() 8 | } 9 | } -------------------------------------------------------------------------------- /src/lib/components/ui/dialog/dialog-portal.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/routes/+page.server.ts: -------------------------------------------------------------------------------- 1 | /** @type {import('./$types').PageServerLoad} */ 2 | export async function load({ fetch }) { 3 | const res = await fetch('/api/get-communities'); 4 | const data = await res.json() 5 | 6 | return { 7 | allCommunities: data 8 | }; 9 | } -------------------------------------------------------------------------------- /src/routes/api/get-communities/+server.ts: -------------------------------------------------------------------------------- 1 | import { json } from '@sveltejs/kit'; 2 | import { Community } from '../../../models/community.model'; 3 | 4 | export async function GET() { 5 | const fetchedCommunities = await Community.find().sort({_id: -1}) 6 | return json(fetchedCommunities); 7 | } -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | LOGLIB_API_KEY= // LogLib.io key 2 | AUTH_SECRET= // mysecret 3 | GITHUB_CLIENT_ID= // your github client id 4 | GITHUB_CLIENT_SECRET= // your github client secret 5 | SECRET_MONGO_URI= // your mongo uri mongodb+srv://USERNAME:PASSWORD@DATABASE/COLLECTION?retryWrites=true&w=majority 6 | 7 | -------------------------------------------------------------------------------- /src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://kit.svelte.dev/docs/types#app 2 | // for information about these interfaces 3 | declare global { 4 | namespace App { 5 | // interface Error {} 6 | // interface Locals {} 7 | // interface PageData {} 8 | // interface PageState {} 9 | // interface Platform {} 10 | } 11 | } 12 | 13 | export {}; 14 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://shadcn-svelte.com/schema.json", 3 | "style": "default", 4 | "tailwind": { 5 | "config": "tailwind.config.js", 6 | "css": "src\\app.css", 7 | "baseColor": "zinc" 8 | }, 9 | "aliases": { 10 | "components": "$lib/components", 11 | "utils": "$lib/utils" 12 | }, 13 | "typescript": true 14 | } -------------------------------------------------------------------------------- /src/routes/api/get-community/+server.ts: -------------------------------------------------------------------------------- 1 | import { json } from '@sveltejs/kit'; 2 | import { Community } from '../../../models/community.model'; 3 | 4 | 5 | export async function GET({ request, url }) { 6 | const link = url.searchParams.get('link') 7 | const fetchedCommunity = await Community.find({ link }); 8 | // console.log(fetchedCommunity) 9 | return json(fetchedCommunity); 10 | } -------------------------------------------------------------------------------- /src/lib/components/ui/select/select-separator.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | // List of extensions which should be recommended for users of this workspace. 5 | "recommendations": [ 6 | "dbaeumer.vscode-eslint", 7 | "esbenp.prettier-vscode", 8 | "svelte.svelte-vscode" 9 | ], 10 | } -------------------------------------------------------------------------------- /src/lib/components/ui/dialog/dialog-header.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 | 13 |
14 | -------------------------------------------------------------------------------- /src/lib/components/ui/dialog/dialog-footer.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
15 | 16 |
17 | -------------------------------------------------------------------------------- /src/lib/components/ui/select/select-label.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/lib/components/ui/dialog/dialog-title.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/lib/components/ui/dialog/dialog-description.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/store/store.ts: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | 3 | type Community = { 4 | name: string, 5 | link: string, 6 | description: string, 7 | owner: string | null | undefined, 8 | tags: string[], 9 | type: string, 10 | }; 11 | 12 | export const currentCommunityStore = writable({}); 13 | export const allCommunitiesStore = writable([]); 14 | export const filteredTagsStore = writable([]); 15 | export const visitorCountStore = writable(0); 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/routes/[username]/+page.server.ts: -------------------------------------------------------------------------------- 1 | /** @type {import('./$types').PageServerLoad} */ 2 | export async function load({ fetch, params }) { 3 | const username = params.username 4 | const community = await fetch(`/api/get-community?link=${username}`) 5 | const res = await community.json() 6 | 7 | const allCommunities = await fetch('/api/get-communities'); 8 | const res2 = await allCommunities.json() 9 | 10 | return ({ 11 | community: res, 12 | allCommunities: res2 13 | }); 14 | } -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | body { 6 | color: aliceblue; 7 | background-color: #09090b; 8 | } 9 | 10 | 11 | @layer utilities { 12 | /* Hide scrollbar for Chrome, Safari and Opera */ 13 | .no-scrollbar::-webkit-scrollbar { 14 | display: none; 15 | } 16 | /* Hide scrollbar for IE, Edge and Firefox */ 17 | .no-scrollbar { 18 | -ms-overflow-style: none; /* IE and Edge */ 19 | scrollbar-width: none; /* Firefox */ 20 | } 21 | } 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/components/socials.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
8 |
9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 |
17 |
-------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official bun image as the base image 2 | FROM oven/bun:1 as base 3 | 4 | # Set the working directory 5 | WORKDIR /app 6 | 7 | # Copy the package.json, bun.lockb, and other necessary files 8 | COPY package.json bun.lockb ./ 9 | COPY .env .env 10 | 11 | # Copy the rest of the application code 12 | COPY . . 13 | 14 | # Install dependencies using bun 15 | RUN bun install 16 | 17 | # Build the SvelteKit app 18 | RUN bun run build 19 | 20 | # Expose the port the app runs on 21 | EXPOSE 5173 22 | 23 | # Define the command to run the app 24 | CMD ["bun", "run", "dev"] 25 | -------------------------------------------------------------------------------- /src/models/community.model.ts: -------------------------------------------------------------------------------- 1 | import * as mongoose from 'mongoose'; 2 | 3 | 4 | const CommunityModel = new mongoose.Schema( 5 | { 6 | name: {type: String, required: true}, 7 | link: {type: String, required: true, unique: true}, 8 | description: {type: String}, 9 | owner: { type: String, default: ''}, 10 | tags: [ 11 | {type: String} 12 | ], 13 | type: { type: String } 14 | }, 15 | { timestamps: true} 16 | ); 17 | 18 | export type Community = mongoose.InferSchemaType; 19 | export const Community = mongoose.model('Community', CommunityModel); -------------------------------------------------------------------------------- /src/lib/components/ui/label/label.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/components/signInButton.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | {#if $page.data.session} 7 |
8 | {#if $page.data.session.user?.image} 9 | profile pic 10 | 11 | {/if} 12 | 13 |
14 | 15 | {:else} 16 | 17 | {/if} 18 | -------------------------------------------------------------------------------- /src/routes/api/create-community/+server.ts: -------------------------------------------------------------------------------- 1 | import { json } from '@sveltejs/kit'; 2 | import { Community } from '../../../models/community.model'; 3 | 4 | export async function POST({ request, }) { 5 | const body = await request.json() 6 | body.link = body.link.toString().toLowerCase() 7 | // console.log("The body: ",body) 8 | const newCommunity = new Community({...body}) 9 | 10 | try { 11 | await newCommunity.save(); 12 | return json({ status: 200, body: "Successfully created community"}); 13 | } catch (error) { 14 | console.error(error); 15 | return json({ status: 500, body: "Error creating document"}); 16 | } 17 | } -------------------------------------------------------------------------------- /src/lib/db/db.ts: -------------------------------------------------------------------------------- 1 | import mongoose, { Mongoose } from 'mongoose'; 2 | import { SECRET_MONGO_URI } from '$env/static/private'; 3 | 4 | interface Connection { 5 | isConnected?: number; 6 | } 7 | 8 | const connection: Connection = {}; 9 | 10 | const dbConnect: () => Promise = async () => { 11 | if (connection.isConnected) { 12 | return; 13 | } 14 | if (!SECRET_MONGO_URI) { 15 | throw new Error('Please define the SECRET_MONGO_URI environment variable inside .env'); 16 | } 17 | 18 | const db: Mongoose = await mongoose.connect(SECRET_MONGO_URI); 19 | 20 | connection.isConnected = db.connections[0].readyState; 21 | }; 22 | 23 | export { dbConnect }; -------------------------------------------------------------------------------- /src/lib/components/ui/dialog/dialog-overlay.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 22 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type { import("eslint").Linter.Config } */ 2 | module.exports = { 3 | root: true, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:svelte/recommended', 8 | 'prettier' 9 | ], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['@typescript-eslint'], 12 | parserOptions: { 13 | sourceType: 'module', 14 | ecmaVersion: 2020, 15 | extraFileExtensions: ['.svelte'] 16 | }, 17 | env: { 18 | browser: true, 19 | es2017: true, 20 | node: true 21 | }, 22 | overrides: [ 23 | { 24 | files: ['*.svelte'], 25 | parser: 'svelte-eslint-parser', 26 | parserOptions: { 27 | parser: '@typescript-eslint/parser' 28 | } 29 | } 30 | ] 31 | }; 32 | -------------------------------------------------------------------------------- /src/lib/components/ui/button/button.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-auto'; 2 | // import adapter from "svelte-adapter-bun"; 3 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 4 | 5 | /** @type {import('@sveltejs/kit').Config} */ 6 | const config = { 7 | // Consult https://kit.svelte.dev/docs/integrations#preprocessors 8 | // for more information about preprocessors 9 | preprocess: vitePreprocess(), 10 | 11 | kit: { 12 | // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. 13 | // If your environment is not supported, or you settled on a specific environment, switch out the adapter. 14 | // See https://kit.svelte.dev/docs/adapters for more information about adapters. 15 | adapter: adapter() 16 | } 17 | }; 18 | 19 | export default config; 20 | -------------------------------------------------------------------------------- /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 | "moduleResolution": "bundler", 13 | "allowImportingTsExtensions": true, 14 | "noImplicitAny": false 15 | } 16 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias 17 | // except $lib which is handled by https://kit.svelte.dev/docs/configuration#files 18 | // 19 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 20 | // from the referenced tsconfig.json - TypeScript does not merge them in 21 | } 22 | -------------------------------------------------------------------------------- /src/lib/components/ui/select/index.ts: -------------------------------------------------------------------------------- 1 | import { Select as SelectPrimitive } from "bits-ui"; 2 | 3 | import Label from "./select-label.svelte"; 4 | import Item from "./select-item.svelte"; 5 | import Content from "./select-content.svelte"; 6 | import Trigger from "./select-trigger.svelte"; 7 | import Separator from "./select-separator.svelte"; 8 | 9 | const Root = SelectPrimitive.Root; 10 | const Group = SelectPrimitive.Group; 11 | const Input = SelectPrimitive.Input; 12 | const Value = SelectPrimitive.Value; 13 | 14 | export { 15 | Root, 16 | Group, 17 | Input, 18 | Label, 19 | Item, 20 | Value, 21 | Content, 22 | Trigger, 23 | Separator, 24 | // 25 | Root as Select, 26 | Group as SelectGroup, 27 | Input as SelectInput, 28 | Label as SelectLabel, 29 | Item as SelectItem, 30 | Value as SelectValue, 31 | Content as SelectContent, 32 | Trigger as SelectTrigger, 33 | Separator as SelectSeparator, 34 | }; 35 | -------------------------------------------------------------------------------- /src/lib/components/ui/input/index.ts: -------------------------------------------------------------------------------- 1 | import Root from "./input.svelte"; 2 | 3 | export type FormInputEvent = T & { 4 | currentTarget: EventTarget & HTMLInputElement; 5 | }; 6 | export type InputEvents = { 7 | blur: FormInputEvent; 8 | change: FormInputEvent; 9 | click: FormInputEvent; 10 | focus: FormInputEvent; 11 | focusin: FormInputEvent; 12 | focusout: FormInputEvent; 13 | keydown: FormInputEvent; 14 | keypress: FormInputEvent; 15 | keyup: FormInputEvent; 16 | mouseover: FormInputEvent; 17 | mouseenter: FormInputEvent; 18 | mouseleave: FormInputEvent; 19 | paste: FormInputEvent; 20 | input: FormInputEvent; 21 | wheel: FormInputEvent; 22 | }; 23 | 24 | export { 25 | Root, 26 | // 27 | Root as Input, 28 | }; 29 | -------------------------------------------------------------------------------- /src/lib/components/ui/select/select-trigger.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | span]:line-clamp-1", 16 | className 17 | )} 18 | {...$$restProps} 19 | let:builder 20 | on:click 21 | on:keydown 22 | > 23 | 24 |
25 | 26 |
27 |
28 | -------------------------------------------------------------------------------- /src/hooks.server.ts: -------------------------------------------------------------------------------- 1 | import { type Handle } from "@sveltejs/kit"; 2 | import { dbConnect } from '$lib/db/db'; 3 | import { SvelteKitAuth } from '@auth/sveltekit'; 4 | import GitHub from "@auth/sveltekit/providers/github"; 5 | 6 | 7 | import { GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET } from '$env/static/private'; 8 | 9 | // Connect DB 10 | export const handle: Handle = async ({ event, resolve }) => { 11 | 12 | try { 13 | await dbConnect(); 14 | console.log("DB connected"); 15 | } catch (error) { 16 | console.error("DB connection error:", error); 17 | } 18 | 19 | const githubProvider = GitHub({ 20 | clientId: GITHUB_CLIENT_ID, 21 | clientSecret: GITHUB_CLIENT_SECRET 22 | }); 23 | 24 | 25 | const authConfig = { 26 | providers: [githubProvider], 27 | trustHost: true, 28 | secret: "true" 29 | }; 30 | 31 | // Initialize authentication middleware 32 | const auth = SvelteKitAuth(authConfig); 33 | 34 | return auth.handle({ event, resolve }); 35 | }; 36 | -------------------------------------------------------------------------------- /src/lib/components/ui/dialog/index.ts: -------------------------------------------------------------------------------- 1 | import { Dialog as DialogPrimitive } from "bits-ui"; 2 | 3 | import Title from "./dialog-title.svelte"; 4 | import Portal from "./dialog-portal.svelte"; 5 | import Footer from "./dialog-footer.svelte"; 6 | import Header from "./dialog-header.svelte"; 7 | import Overlay from "./dialog-overlay.svelte"; 8 | import Content from "./dialog-content.svelte"; 9 | import Description from "./dialog-description.svelte"; 10 | 11 | const Root = DialogPrimitive.Root; 12 | const Trigger = DialogPrimitive.Trigger; 13 | const Close = DialogPrimitive.Close; 14 | 15 | export { 16 | Root, 17 | Title, 18 | Portal, 19 | Footer, 20 | Header, 21 | Trigger, 22 | Overlay, 23 | Content, 24 | Description, 25 | Close, 26 | // 27 | Root as Dialog, 28 | Title as DialogTitle, 29 | Portal as DialogPortal, 30 | Footer as DialogFooter, 31 | Header as DialogHeader, 32 | Trigger as DialogTrigger, 33 | Overlay as DialogOverlay, 34 | Content as DialogContent, 35 | Description as DialogDescription, 36 | Close as DialogClose, 37 | }; 38 | -------------------------------------------------------------------------------- /src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | export const TAGS = [ 2 | 'Spiritual', 3 | 'Tech', 4 | 'News', 5 | 'Coding', 6 | 'Books', 7 | 'Product', 8 | 'Startup', 9 | 'Company', 10 | 'School', 11 | 'Food', 12 | 'Pets', 13 | 'Music', 14 | 'Crafts', 15 | 'Travel', 16 | 'Science', 17 | 'Fitness', 18 | 'NSFW', 19 | 'Education', 20 | 'Photography', 21 | 'Humor', 22 | 'Memes', 23 | 'Hardware', 24 | 'Politics', 25 | 'Nature', 26 | 'Aesthetics', 27 | 'Wallpapers', 28 | 'Esoteric', 29 | 'Literature', 30 | 'Quotes', 31 | 'Poems', 32 | 'Business', 33 | 'Crypto', 34 | 'Dating', 35 | 'Charity', 36 | 'NGO', 37 | 'Club', 38 | 'FOSS', 39 | 'Dumps', 40 | 'Gaming', 41 | 'Podcast', 42 | 'Resources', 43 | 'Language', 44 | 'Governmental', 45 | 'e-Commerce', 46 | 'Cybersecurity', 47 | 'Hacking', 48 | 'Competitive Programming', 49 | 'Bible Study', 50 | 'Philosphy', 51 | 'Thoughts', 52 | 'Opportunities', 53 | 'Jobs', 54 | 'AI / ML', 55 | 'Film / TV', 56 | 'Fashion / Beauty', 57 | 'Art / Design', 58 | 'Games / Apps', 59 | 'Unsorted', 60 | 'untagged', 61 | 'other' 62 | ]; 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Dagmawi Babi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/components/header.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 |
22 |

25 | #{allCommunities.length} Communities Added! 26 |

27 | 28 |

31 | Discover the Best
Telegram Communities 32 |

33 |
34 |
35 |

Here are some of the most incredible channels and groups submitted by the public. 36 | 37 |

38 |

39 |
40 |
41 | -------------------------------------------------------------------------------- /src/lib/components/ui/select/select-content.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 | 36 |
37 | 38 |
39 |
40 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 18 | 19 | 20 | 22 | 23 | 24 | 26 | 27 | 28 | 29 | %sveltekit.head% 30 | 31 | 32 | 33 |
%sveltekit.body%
34 | 35 | 36 | -------------------------------------------------------------------------------- /src/lib/components/ui/input/input.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | 42 | -------------------------------------------------------------------------------- /src/lib/components/ui/select/select-item.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | {label || value} 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/lib/components/ui/dialog/dialog-content.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 28 | 29 | 32 | 33 | Close 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tggallery", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite dev", 7 | "build": "vite build", 8 | "preview": "vite preview", 9 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 10 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 11 | "lint": "prettier --check . && eslint .", 12 | "format": "prettier --write ." 13 | }, 14 | "devDependencies": { 15 | "@sveltejs/adapter-auto": "^3.0.0", 16 | "@sveltejs/kit": "^2.0.0", 17 | "@sveltejs/vite-plugin-svelte": "^3.0.0", 18 | "@types/eslint": "^8.56.0", 19 | "@typescript-eslint/eslint-plugin": "^7.0.0", 20 | "@typescript-eslint/parser": "^7.0.0", 21 | "autoprefixer": "^10.4.19", 22 | "eslint": "^8.56.0", 23 | "eslint-config-prettier": "^9.1.0", 24 | "eslint-plugin-svelte": "^2.35.1", 25 | "postcss": "^8.4.38", 26 | "prettier": "^3.1.1", 27 | "prettier-plugin-svelte": "^3.1.2", 28 | "svelte": "^4.2.7", 29 | "svelte-adapter-bun": "^0.5.2", 30 | "svelte-check": "^3.6.0", 31 | "tailwindcss": "^3.4.3", 32 | "tslib": "^2.4.1", 33 | "typescript": "^5.0.0", 34 | "vite": "^5.0.3" 35 | }, 36 | "type": "module", 37 | "trustedDependencies": [ 38 | "svelte-preprocess" 39 | ], 40 | "dependencies": { 41 | "@auth/sveltekit": "^1.0.1", 42 | "@loglib/tracker": "^0.8.0", 43 | "axios": "^1.6.8", 44 | "bits-ui": "^0.21.7", 45 | "clsx": "^2.1.1", 46 | "lucide-svelte": "^0.378.0", 47 | "mongoose": "^8.3.4", 48 | "tailwind-merge": "^2.3.0", 49 | "tailwind-variants": "^0.2.1" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/lib/components/ui/button/index.ts: -------------------------------------------------------------------------------- 1 | import { type VariantProps, tv } from "tailwind-variants"; 2 | import type { Button as ButtonPrimitive } from "bits-ui"; 3 | import Root from "./button.svelte"; 4 | 5 | const buttonVariants = tv({ 6 | base: "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", 7 | variants: { 8 | variant: { 9 | default: "bg-primary text-primary-foreground hover:bg-primary/90", 10 | destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90", 11 | outline: 12 | "border border-input bg-background hover:bg-accent hover:text-accent-foreground", 13 | secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", 14 | ghost: "hover:bg-accent hover:text-accent-foreground", 15 | link: "text-primary underline-offset-4 hover:underline", 16 | }, 17 | size: { 18 | default: "h-10 px-4 py-2", 19 | sm: "h-9 rounded-md px-3", 20 | lg: "h-11 rounded-md px-8", 21 | icon: "h-10 w-10", 22 | }, 23 | }, 24 | defaultVariants: { 25 | variant: "default", 26 | size: "default", 27 | }, 28 | }); 29 | 30 | type Variant = VariantProps["variant"]; 31 | type Size = VariantProps["size"]; 32 | 33 | type Props = ButtonPrimitive.Props & { 34 | variant?: Variant; 35 | size?: Size; 36 | }; 37 | 38 | type Events = ButtonPrimitive.Events; 39 | 40 | export { 41 | Root, 42 | type Props, 43 | type Events, 44 | // 45 | Root as Button, 46 | type Props as ButtonProps, 47 | type Events as ButtonEvents, 48 | buttonVariants, 49 | }; 50 | -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | import { cubicOut } from "svelte/easing"; 4 | import type { TransitionConfig } from "svelte/transition"; 5 | 6 | export function cn(...inputs: ClassValue[]) { 7 | return twMerge(clsx(inputs)); 8 | } 9 | 10 | type FlyAndScaleParams = { 11 | y?: number; 12 | x?: number; 13 | start?: number; 14 | duration?: number; 15 | }; 16 | 17 | export const flyAndScale = ( 18 | node: Element, 19 | params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 } 20 | ): TransitionConfig => { 21 | const style = getComputedStyle(node); 22 | const transform = style.transform === "none" ? "" : style.transform; 23 | 24 | const scaleConversion = ( 25 | valueA: number, 26 | scaleA: [number, number], 27 | scaleB: [number, number] 28 | ) => { 29 | const [minA, maxA] = scaleA; 30 | const [minB, maxB] = scaleB; 31 | 32 | const percentage = (valueA - minA) / (maxA - minA); 33 | const valueB = percentage * (maxB - minB) + minB; 34 | 35 | return valueB; 36 | }; 37 | 38 | const styleToString = ( 39 | style: Record 40 | ): string => { 41 | return Object.keys(style).reduce((str, key) => { 42 | if (style[key] === undefined) return str; 43 | return str + `${key}:${style[key]};`; 44 | }, ""); 45 | }; 46 | 47 | return { 48 | duration: params.duration ?? 200, 49 | delay: 0, 50 | css: (t) => { 51 | const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]); 52 | const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]); 53 | const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]); 54 | 55 | return styleToString({ 56 | transform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`, 57 | opacity: t 58 | }); 59 | }, 60 | easing: cubicOut 61 | }; 62 | }; -------------------------------------------------------------------------------- /src/components/footer.svelte: -------------------------------------------------------------------------------- 1 | 33 | 34 | 37 | 38 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | import { fontFamily } from "tailwindcss/defaultTheme"; 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | const config = { 5 | darkMode: ["class"], 6 | content: ["./src/**/*.{html,js,svelte,ts}"], 7 | safelist: ["dark"], 8 | theme: { 9 | container: { 10 | center: true, 11 | padding: "2rem", 12 | screens: { 13 | "2xl": "1400px" 14 | } 15 | }, 16 | extend: { 17 | colors: { 18 | border: "hsl(var(--border) / )", 19 | input: "hsl(var(--input) / )", 20 | ring: "hsl(var(--ring) / )", 21 | background: "hsl(var(--background) / )", 22 | foreground: "hsl(var(--foreground) / )", 23 | primary: { 24 | DEFAULT: "hsl(var(--primary) / )", 25 | foreground: "hsl(var(--primary-foreground) / )" 26 | }, 27 | secondary: { 28 | DEFAULT: "hsl(var(--secondary) / )", 29 | foreground: "hsl(var(--secondary-foreground) / )" 30 | }, 31 | destructive: { 32 | DEFAULT: "hsl(var(--destructive) / )", 33 | foreground: "hsl(var(--destructive-foreground) / )" 34 | }, 35 | muted: { 36 | DEFAULT: "hsl(var(--muted) / )", 37 | foreground: "hsl(var(--muted-foreground) / )" 38 | }, 39 | accent: { 40 | DEFAULT: "hsl(var(--accent) / )", 41 | foreground: "hsl(var(--accent-foreground) / )" 42 | }, 43 | popover: { 44 | DEFAULT: "hsl(var(--popover) / )", 45 | foreground: "hsl(var(--popover-foreground) / )" 46 | }, 47 | card: { 48 | DEFAULT: "hsl(var(--card) / )", 49 | foreground: "hsl(var(--card-foreground) / )" 50 | } 51 | }, 52 | borderRadius: { 53 | lg: "var(--radius)", 54 | md: "calc(var(--radius) - 2px)", 55 | sm: "calc(var(--radius) - 4px)" 56 | }, 57 | fontFamily: { 58 | sans: [...fontFamily.sans] 59 | } 60 | } 61 | }, 62 | }; 63 | 64 | export default config; 65 | -------------------------------------------------------------------------------- /src/components/eachCommunity.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 |
13 |
14 |
15 | 16 | {#if community.type == "channel"} 17 |

{community.name}

18 | {:else} 19 |

{community.name}

20 | {/if} 21 | 22 | 23 | {#if community.type == "channel"} 24 |
25 | 26 |
27 | {:else} 28 |
29 | 30 | 31 |
32 | {/if} 33 |
34 | 35 | 36 |

{community.description}

37 |
38 | 39 |
40 | {#if community.tags.length > 0} 41 | {#each community.tags.slice(0,3) as tag} 42 |
43 | {#if community.type == "channel"} 44 |
{tag}
45 | {:else} 46 |
{tag}
47 | {/if} 48 |
49 | {/each} 50 | {/if} 51 |
52 | 53 |
-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ✨ Telegram Community Gallery ✨ 2 | ![Screenshot](/static/Screenshot.jpg) 3 | 4 | **[Telegram Community Gallery](https://telegram.dagmawi.dev)** is a platform where users can discover and submit their favorite Telegram channels and groups, creating a vibrant space for community exploration. Whether you're a seasoned Telegram user or just diving into the world of Telegram communities, this platform is designed with you in mind. 😊 5 | 6 | ### Features 🎉 7 | - **Discover Communities**: Explore a wide range of Telegram channels and groups, curated by fellow users. 8 | - **Submit Your Community**: Share your favorite Telegram communities with the world by submitting them to the gallery. 9 | - **Tagging and Filtering**: Easily find communities that match your interests with powerful tagging and filtering options. 10 | - **Crowdsourced and Open Source**: The platform thrives on community contributions and is completely open source, ensuring transparency and inclusivity. 11 | 12 | ### Tech Stack 💻 13 | Telegram Community Gallery is built using the following technologies: 14 | - **Frontend**: [SvelteKit](https://kit.svelte.dev/) and [Tailwind](https://tailwindcss.com/) 15 | - **Icons**: [Lucide Icons](https://lucide.dev/) 16 | - **Component Library**: [Shadcn-Svelte](https://www.shadcn-svelte.com/) 17 | - **Backend**: [HonoJS](https://hono.dev/) 18 | - **Database**: [MongoDB](https://www.mongodb.com/) 19 | 20 | ### Getting Started 🚀 21 | To get started with Telegram Community Gallery, simply visit our [Website](https://telegram.dagmawi.dev) and start exploring! If you'd like to contribute to the project or report any issues, check out our [GitHub repository.](https://github.com/dagmawibabi/TelegramCommunityGallery) 22 | 23 | ### How to Contribute 🤝 24 | We welcome contributions from the community! Follow these steps to contribute: 25 | 1. **Fork the Repository**: Fork the Telegram Community Gallery repository to your GitHub account. 26 | 2. **Make Changes**: Create a new branch, make your changes, and commit them to your branch. 27 | 3. **Create a Pull Request**: Once your changes are ready, create a detailed pull request (PR) explaining the changes you've made. 28 | 4. **Review and Iterate**: Collaborate with the maintainers to review and iterate on your changes until they are ready to be merged. 29 | 5. **That's it!**: Can't wait to see the wonders you'll be doing. 30 | 31 | For more detailed guidelines on contributing, please refer to our contribution guidelines. 32 | 33 | ### Feedback and Support 💌 34 | Have any questions, suggestions, or need support? Feel free to reach out to us in our discussions tab or [open an issue](https://github.com/dagmawibabi/TelegramCommunityGallery/issues) on GitHub. Your feedback helps us make Telegram Community Gallery even better! 35 | 36 | ### License 📝 37 | Telegram Community Gallery is licensed under the [MIT License](https://github.com/dagmawibabi/TelegramCommunityGallery?tab=MIT-1-ov-file). 38 | 39 | ### Thank You and Enjoy! 🥰 -------------------------------------------------------------------------------- /src/components/radioComponent.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 |
23 |
{legend} 25 |
26 | {#each options as { value, label }} 27 | 33 | 34 | {/each} 35 |
36 | 37 | 151 | -------------------------------------------------------------------------------- /src/routes/[username]/+page.svelte: -------------------------------------------------------------------------------- 1 | 35 | 36 | 37 | {currentCommunity.name} 38 | 39 | 40 | 41 |
42 | 43 |
44 |
45 |
46 | 47 | 48 |
49 |
52 |
55 | 56 | 57 | 58 | 59 |
60 |
61 | 62 | 63 |
66 |
70 | 71 | {#if currentCommunity.type == "channel"} 72 | 75 | {:else} 76 | 79 | {/if} 80 |
81 | {currentCommunity.name} 82 |
83 | 84 | 85 |
86 | {currentCommunity.link} 87 |
88 |
89 | 90 | 91 |
92 | 93 | {#if currentCommunity.description.includes("\n") == true} 94 | {#each currentCommunity.description.split("\n") as letters} 95 | 96 | {letters}  97 | 98 | {/each} 99 | {:else} 100 | {#each currentCommunity.description.split(" ") as letters} 101 | 102 | {letters}  103 | 104 | {/each} 105 | {/if} 106 |
107 | 108 | {#if currentCommunity.owner != ""} 109 | 110 |
111 | Owner: {currentCommunity.owner} 112 |
113 |
114 | {/if} 115 | 116 |
117 | {#each currentCommunity.tags as tag} 118 |
119 |
120 | {tag} 121 |
122 |
123 | {/each} 124 | 125 |
126 | 127 |
128 |
129 | 130 | 131 |
132 |
133 | 134 | 135 |
136 | 137 | 138 | -------------------------------------------------------------------------------- /src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 90 | 91 | 92 | Telegram Community Gallery 93 | 94 | 95 |
96 |
99 |
102 | 103 |
106 |
111 |
116 |
117 |
118 | 119 |
120 | 121 |
122 | 123 |
124 |
125 | 126 |
127 | 128 | 129 | 130 |
131 | 132 | 138 |
139 | 140 |
141 | {#each TAGS as tag} 142 |
147 | {#if filterFeedTags.indexOf(tag) == -1} 148 | filterFeed(tag)} 150 | class="lowercase pb-1 bg-[#09090b] rounded-full border border-zinc-800 h-fit w-fit px-4 text-sm text-zinc-400 hover:bg-blue-500 cursor-pointer hover:text-black line-clamp-1 151 | md:text-base md:px-3 152 | " 153 | > 154 | {tag} 155 | 156 | {:else} 157 | filterFeed(tag)} 159 | class="lowercase pb-1 bg-blue-500 rounded-full border border-zinc-800 h-fit w-fit px-3 text-base text-black font-semibold cursor-pointer hover:text-black line-clamp-1" 160 | > 161 | {tag} 162 | 163 | {/if} 164 |
165 | {/each} 166 |
clearFilter()} 169 | > 170 | 171 |
172 |
173 | 174 |
175 | 176 | 177 | 178 | 179 | 180 | { 184 | isChannels.set('all'); 185 | }} 186 | > 187 | All 188 | 189 | { 193 | isChannels.set('channel'); 194 | }} 195 | > 196 | Channels 197 | 198 | { 202 | isChannels.set('group'); 203 | }} 204 | > 205 | Groups 206 | 207 | 208 | 209 |
210 |
211 | 212 | {#if $allCommunitiesStore.length > 0} 213 | {#if $filteredCommunities.length > 0} 214 |
219 | {#each $filteredCommunities as community} 220 | currentCommunityStore.set(community)}> 221 | 222 | 223 | {/each} 224 |
225 | {:else} 226 |
227 | 228 | No communities found matching your criteria. 229 | 230 |
231 | {/if} 232 | {:else} 233 |
234 | Loading Communities... 235 |
236 | {/if} 237 |
238 |
239 |
240 | -------------------------------------------------------------------------------- /src/components/addCommunity.svelte: -------------------------------------------------------------------------------- 1 | 121 | 122 |
123 | 124 | 125 | 126 |
129 | 130 |

135 | Submit Your Channel / Group 136 |

137 |
138 |
139 | 140 | 143 | 144 | 145 | Add Channel / Group 146 | 147 | Add your community and click save when you're done! 148 | 149 | 150 | 151 | 152 |
153 | 154 | 162 | 163 | 164 | 172 |
173 |
    174 | {#each errors as error} 175 |
  • {error}
  • 176 | {/each} 177 |
178 |
179 | 182 |