feat(rewrite): Rewrote the whole thing in ReactJS 💅 (#22)
* feat(rewrite): Rewrote the whole thing in ReactJS 💅
* chore(docs): Updated
6
.babelrc
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"presets": [
|
||||
"@babel/preset-env",
|
||||
"@babel/preset-react"
|
||||
]
|
||||
}
|
23
.env.example
Normal file
@ -0,0 +1,23 @@
|
||||
# this is used for local debugging
|
||||
# RAZZLE_ variable is passed to the server
|
||||
# copy this to .env for testing
|
||||
|
||||
RAZZLE_META_TITLE=Techno Tim
|
||||
RAZZLE_META_DESCRIPTION=Techno Tim Link page
|
||||
RAZZLE_META_AUTHOR=Techno Tim
|
||||
RAZZLE_THEME=Dark
|
||||
RAZZLE_FAVICON_URL=https://pbs.twimg.com/profile_images/1286144221217316864/qIAsKOpB_200x200.jpg
|
||||
RAZZLE_AVATAR_URL=https://pbs.twimg.com/profile_images/1286144221217316864/qIAsKOpB_200x200.jpg
|
||||
RAZZLE_AVATAR_2X_URL=https://pbs.twimg.com/profile_images/1286144221217316864/qIAsKOpB_400x400.jpg
|
||||
RAZZLE_AVATAR_ALT=Techno Tim Profile Pic
|
||||
RAZZLE_NAME=TechnoTim
|
||||
RAZZLE_BIO=Hey! Just a place where you can connect with me!
|
||||
RAZZLE_GITHUB=https://github.com/timothystewart6
|
||||
RAZZLE_TWITTER=https://twitter.com/TechnoTimLive
|
||||
RAZZLE_INSTAGRAM=https://www.instagram.com/techno.tim
|
||||
RAZZLE_YOUTUBE=https://www.youtube.com/channel/UCOk-gHyjcWZNj3Br4oxwh0A
|
||||
RAZZLE_TWITCH=https://www.twitch.tv/technotim/
|
||||
RAZZLE_DISCORD=https://discord.gg/DJKexrJ
|
||||
RAZZLE_TIKTOK=https://www.tiktok.com/@technotim
|
||||
RAZZLE_KIT=https://kit.co/TechnoTim
|
||||
RAZZLE_FOOTER=Thanks for stopping by!
|
13
.eslintignore
Normal file
@ -0,0 +1,13 @@
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
.DS_Store
|
||||
|
||||
coverage
|
||||
node_modules
|
||||
build
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
cache
|
42
.eslintrc
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"parser": "@babel/eslint-parser",
|
||||
"settings": {
|
||||
"react": {
|
||||
"pragma": "React",
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"plugins": ["jsx","prettier"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"env": {
|
||||
"jest": true,
|
||||
"jasmine": true,
|
||||
"node": true,
|
||||
"browser": true,
|
||||
"es6": true
|
||||
|
||||
},
|
||||
"rules": {
|
||||
"prettier/prettier":
|
||||
["error", {
|
||||
"singleQuote": true,
|
||||
"jsxSingleQuote": false,
|
||||
"arrowParens": "avoid",
|
||||
"semi": true,
|
||||
"trailingComma": "all",
|
||||
"spaceAfterFunction": true
|
||||
}],
|
||||
"no-console": ["error", { "allow": ["warn", "error"] }],
|
||||
"no-alert": "error",
|
||||
"no-debugger": "error",
|
||||
"prefer-const": "error",
|
||||
"prefer-arrow-callback": "error",
|
||||
"no-unused-vars": ["error", {"args": "none"}],
|
||||
"react/prop-types": 0,
|
||||
"react/no-unescaped-entities": 0
|
||||
}
|
||||
}
|
2
.github/workflows/main.yml
vendored
@ -20,6 +20,8 @@ jobs:
|
||||
- name: Install Dependencies, Test, and Build
|
||||
run: |
|
||||
yarn install --frozen-lockfile --check-files
|
||||
yarn lint
|
||||
yarn test
|
||||
env:
|
||||
CI: true
|
||||
|
||||
|
2
.github/workflows/pull-request.yml
vendored
@ -23,5 +23,7 @@ jobs:
|
||||
- name: Install Dependencies, Test, and Build
|
||||
run: |
|
||||
yarn install --frozen-lockfile --check-files
|
||||
yarn lint
|
||||
yarn test
|
||||
env:
|
||||
CI: true
|
30
.gitignore
vendored
@ -1,26 +1,14 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
.DS_Store
|
||||
|
||||
coverage
|
||||
node_modules
|
||||
build
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# this is is the output
|
||||
www/index.html
|
||||
.env
|
||||
cache
|
13
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Attach to dev server",
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"protocol": "inspector",
|
||||
"address": "localhost",
|
||||
"port": 9229
|
||||
}
|
||||
]
|
||||
}
|
33
Dockerfile
@ -1,18 +1,23 @@
|
||||
FROM node:14.17.4-alpine
|
||||
FROM node:14.17.4-alpine AS node-build
|
||||
RUN apk --no-cache add \
|
||||
gettext \
|
||||
bash
|
||||
ENV NODE_ENV=production
|
||||
WORKDIR /usr/src/app
|
||||
COPY package.json yarn.lock ./
|
||||
RUN yarn install --frozen-lockfile --check-files --production=true
|
||||
COPY www ./www
|
||||
COPY template ./template
|
||||
COPY env.js ./
|
||||
COPY app.js ./
|
||||
COPY ./entrypoint.sh /
|
||||
RUN chmod +x /entrypoint.sh
|
||||
EXPOSE 3000
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD [ "node", "app.js" ]
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
COPY package.json ./
|
||||
COPY yarn.lock ./
|
||||
COPY src ./src
|
||||
COPY public ./public
|
||||
RUN yarn install --frozen-lockfile --check-files
|
||||
RUN yarn build --noninteractive
|
||||
RUN yarn install --frozen-lockfile --check-files --production --modules-folder node_modules_prod
|
||||
|
||||
FROM node:14.17.4-alpine
|
||||
WORKDIR /usr/src/app
|
||||
ENV NODE_ENV production
|
||||
RUN mkdir -p /node_modules
|
||||
COPY --from=node-build /usr/src/app/build ./build
|
||||
COPY --from=node-build /usr/src/app/node_modules_prod ./node_modules
|
||||
EXPOSE 3000
|
||||
CMD [ "node", "build/server.js" ]
|
||||
|
||||
|
11
README.md
@ -1,7 +1,7 @@
|
||||
# 🔗 LittleLink-Server
|
||||
|
||||
This project is based on the great work from [littlelink](https://github.com/sethcottle/littlelink)
|
||||
It takes the same simple approach to a link page and hosts it within a nodeJS server containerized for you to use. Now, customizing `LittleLink` with `littlelink-server` is as easy as passing in some environment variables. If you need help configuring this, please see [this video that explains everything](https://www.youtube.com/watch?v=42SqfI_AjXU).
|
||||
It takes the same simple approach to a link page and hosts it within a NodeJS server with React Server Side Rendering, containerized for you to use. Now, customizing `LittleLink` with `littlelink-server` is as easy as passing in some environment variables. If you need help configuring this, please see [this video that explains everything](https://www.youtube.com/watch?v=42SqfI_AjXU).
|
||||
|
||||
# 🚀 Getting Started
|
||||
|
||||
@ -45,7 +45,9 @@ services:
|
||||
# - MEDIUM=https://medium.com
|
||||
# - PINTEREST=https://www.pinterest.com/
|
||||
# - EMAIL=you@example.com
|
||||
# - EMAIL_TEXT=Email Me!
|
||||
# - EMAIL_ALT=you@example.com
|
||||
# - EMAIL_ALT_TEXT=Email me!
|
||||
# - SOUND_CLOUD=https://souncloud.com
|
||||
# - FIGMA=https://figma.com
|
||||
# - TELEGRAM=https://telegram.org/
|
||||
@ -87,20 +89,23 @@ docker run -d \
|
||||
-e DISCORD='https://discord.gg/DJKexrJ' \
|
||||
-e TIKTOK='https://www.tiktok.com/@technotim' \
|
||||
-e KIT='https://kit.co/TechnoTim' \
|
||||
-e EMAIL='someone@example.com' \
|
||||
-e EMAIL_TEXT='Email me!' \
|
||||
-e FOOTER=Thanks for stopping by! \
|
||||
--restart unless-stopped \
|
||||
ghcr.io/techno-tim/littlelink-server:latest
|
||||
```
|
||||
|
||||
## Kubernetes
|
||||
|
||||
[unoffical helm chart provided by k8s-at-home](https://github.com/k8s-at-home/charts/tree/master/charts/stable/littlelink-server)
|
||||
[Unofficial helm chart provided by k8s-at-home](https://github.com/k8s-at-home/charts/tree/master/charts/stable/littlelink-server)
|
||||
|
||||
```
|
||||
helm repo add k8s-at-home https://k8s-at-home.com/charts/
|
||||
helm repo update
|
||||
helm install littlelink-server \
|
||||
--set env.TZ="America/New York" \
|
||||
--set env.META_TITLE="Technotim"
|
||||
--set env.META_TITLE="TechnoTim"
|
||||
k8s-at-home/littlelink-server
|
||||
```
|
||||
Or use a values.yaml files
|
||||
|
31
app.js
@ -1,31 +0,0 @@
|
||||
const express = require('express')
|
||||
const morgan = require('morgan')
|
||||
const compression = require('compression')
|
||||
const fs = require('fs')
|
||||
const jsdom = require('jsdom')
|
||||
const useEnv = require('./env')
|
||||
|
||||
fs.readFile('./template/index.html', 'utf8', (err, file) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
const { JSDOM } = jsdom
|
||||
const dom = new JSDOM(file);
|
||||
const html = "<!DOCTYPE html>\n" + useEnv(dom.window.document).documentElement.outerHTML;
|
||||
fs.writeFile('./www/index.html', html, 'utf8', (err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const app = express();
|
||||
app.use(morgan('combined'));
|
||||
app.use(express.static('www'));
|
||||
app.use(compression())
|
||||
|
||||
const server = app.listen(process.env.PORT || 3000, () => {
|
||||
const host = server.address().address
|
||||
const port = server.address().port
|
||||
console.log('Express app listening at http://%s:%s', host, port)
|
||||
})
|
@ -19,13 +19,12 @@ services:
|
||||
- BIO=Hey! Just a place where you can connect with me!
|
||||
- GITHUB=https://github.com/timothystewart6
|
||||
- TWITTER=https://twitter.com/TechnoTimLive
|
||||
- MASTODON=https://mastodon.social/TechnoTimLive
|
||||
- INSTAGRAM=https://www.instagram.com/techno.tim
|
||||
- YOUTUBE=https://www.youtube.com/channel/UCOk-gHyjcWZNj3Br4oxwh0A/
|
||||
- TWITCH=https://www.twitch.tv/technotim/
|
||||
- DISCORD=https://discord.gg/DJKexrJ
|
||||
- KIT=https://kit.co/TechnoTim
|
||||
- FOOTER=Thanks for stopping by!
|
||||
- FOOTER=Thanks for stopping by!!!
|
||||
|
||||
ports:
|
||||
- 8080:3000
|
||||
|
@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
originalfile="/usr/src/app/env.js"
|
||||
tmpfile=$(mktemp)
|
||||
cp --attributes-only --preserve $originalfile $tmpfile
|
||||
cat $originalfile | envsubst | tee $tmpfile && mv $tmpfile $originalfile
|
||||
exec "$@"
|
390
env.js
@ -1,390 +0,0 @@
|
||||
module.exports = useEnv;
|
||||
|
||||
var env = {
|
||||
META_TITLE: '$META_TITLE',
|
||||
META_DESCRIPTION: '$META_DESCRIPTION',
|
||||
META_AUTHOR: '$META_AUTHOR',
|
||||
THEME: '$THEME',
|
||||
FAVICON_URL: '$FAVICON_URL',
|
||||
AVATAR_URL: '$AVATAR_URL',
|
||||
AVATAR_ALT: '$AVATAR_ALT',
|
||||
AVATAR_2X_URL: '$AVATAR_2X_URL',
|
||||
NAME: '$NAME',
|
||||
BIO: '$BIO',
|
||||
GITHUB: '$GITHUB',
|
||||
TWITTER: '$TWITTER',
|
||||
MASTODON: '$MASTODON',
|
||||
MICRO_BLOG: '$MICRO_BLOG',
|
||||
INSTAGRAM: '$INSTAGRAM',
|
||||
FACEBOOK: '$FACEBOOK',
|
||||
FACEBOOK_MESSENGER: '$FACEBOOK_MESSENGER',
|
||||
LINKED_IN: '$LINKED_IN',
|
||||
YOUTUBE: '$YOUTUBE',
|
||||
DISCORD: '$DISCORD',
|
||||
TWITCH: '$TWITCH',
|
||||
PRODUCT_HUNT: '$PRODUCT_HUNT',
|
||||
SNAPCHAT: '$SNAPCHAT',
|
||||
SPOTIFY: '$SPOTIFY',
|
||||
REDDIT: '$REDDIT',
|
||||
MEDIUM: '$MEDIUM',
|
||||
PINTEREST: '$PINTEREST',
|
||||
TIKTOK: '$TIKTOK',
|
||||
EMAIL: '$EMAIL',
|
||||
EMAIL_TEXT: '$EMAIL_TEXT',
|
||||
EMAIL_ALT: '$EMAIL_ALT',
|
||||
EMAIL_ALT_TEXT: '$EMAIL_ALT_TEXT',
|
||||
SOUND_CLOUD: '$SOUND_CLOUD',
|
||||
FIGMA: '$FIGMA',
|
||||
KIT: '$KIT',
|
||||
TELEGRAM: '$TELEGRAM',
|
||||
TUMBLR: '$TUMBLR',
|
||||
STEAM: '$STEAM',
|
||||
VIMEO: '$VIMEO',
|
||||
WORDPRESS: '$WORDPRESS',
|
||||
GOODREADS: '$GOODREADS',
|
||||
SKOOB: '$SKOOB',
|
||||
WHATSAPP: '$WHATSAPP',
|
||||
LETTERBOXD: '$LETTERBOXD',
|
||||
FOOTER: '$FOOTER',
|
||||
|
||||
}
|
||||
|
||||
function useEnv(document) {
|
||||
var metalTitleEl = document.getElementById('meta-title');
|
||||
if (env.META_TITLE) {
|
||||
metalTitleEl.textContent = env.META_TITLE;
|
||||
} else {
|
||||
metalTitleEl.remove()
|
||||
}
|
||||
|
||||
var metalDescriptionEl = document.getElementById('meta-description');
|
||||
if (env.META_DESCRIPTION) {
|
||||
metalDescriptionEl.content = env.META_DESCRIPTION;
|
||||
} else {
|
||||
metalDescriptionEl.remove()
|
||||
}
|
||||
|
||||
var metaAuthorEl = document.getElementById('meta-author');
|
||||
if (env.META_AUTHOR) {
|
||||
metaAuthorEl.content = env.META_AUTHOR;
|
||||
} else {
|
||||
metaAuthorEl.remove()
|
||||
}
|
||||
|
||||
var themeEl = document.getElementById('theme');
|
||||
if (env.THEME && env.THEME.toLocaleLowerCase() === 'dark') {
|
||||
themeEl.href = 'css/skeleton-dark.css';
|
||||
} else {
|
||||
themeEl.href = 'css/skeleton-light.css';
|
||||
}
|
||||
|
||||
var faviconEl = document.getElementById('favicon');
|
||||
if (env.FAVICON_URL) {
|
||||
faviconEl.href = env.FAVICON_URL;
|
||||
}
|
||||
|
||||
var avatarEl = document.getElementById('avatar');
|
||||
if (env.AVATAR_URL) {
|
||||
avatarEl.src = env.AVATAR_URL;
|
||||
avatarEl.alt = env.AVATAR_ALT;
|
||||
avatarEl.srcset = env.AVATAR_2X_URL + ' 2x';
|
||||
} else {
|
||||
avatarEl.remove()
|
||||
}
|
||||
|
||||
var nameEl = document.getElementById('name');
|
||||
if (env.NAME) {
|
||||
nameEl.innerHTML = env.NAME;
|
||||
} else {
|
||||
nameEl.remove()
|
||||
}
|
||||
|
||||
var bioEl = document.getElementById('bio');
|
||||
if (env.BIO) {
|
||||
bioEl.innerHTML = env.BIO;
|
||||
} else {
|
||||
bioEl.remove()
|
||||
}
|
||||
|
||||
var githubEl = document.getElementById('github');
|
||||
if (env.GITHUB) {
|
||||
githubEl.href = env.GITHUB;
|
||||
} else {
|
||||
githubEl.nextElementSibling.remove()
|
||||
githubEl.remove()
|
||||
}
|
||||
|
||||
var twitterEl = document.getElementById('twitter');
|
||||
if (env.TWITTER) {
|
||||
twitterEl.href = env.TWITTER;
|
||||
} else {
|
||||
twitterEl.nextElementSibling.remove()
|
||||
twitterEl.remove()
|
||||
}
|
||||
|
||||
var mastodonEl = document.getElementById('mastodon');
|
||||
if (env.MASTODON) {
|
||||
mastodonEl.href = env.MASTODON;
|
||||
} else {
|
||||
mastodonEl.nextElementSibling.remove()
|
||||
mastodonEl.remove()
|
||||
}
|
||||
|
||||
var microblogEl = document.getElementById('microblog');
|
||||
if (env.MICRO_BLOG) {
|
||||
microblogEl.href = env.MICRO_BLOG;
|
||||
} else {
|
||||
microblogEl.nextElementSibling.remove()
|
||||
microblogEl.remove()
|
||||
}
|
||||
|
||||
var instagramEl = document.getElementById('instagram');
|
||||
if (env.INSTAGRAM) {
|
||||
instagramEl.href = env.INSTAGRAM;
|
||||
} else {
|
||||
instagramEl.nextElementSibling.remove()
|
||||
instagramEl.remove()
|
||||
}
|
||||
|
||||
var facebookEl = document.getElementById('facebook');
|
||||
if (env.FACEBOOK) {
|
||||
facebookEl.href = env.FACEBOOK;
|
||||
} else {
|
||||
facebookEl.nextElementSibling.remove()
|
||||
facebookEl.remove()
|
||||
}
|
||||
|
||||
var facebookMessengerEl = document.getElementById('facebook-messenger');
|
||||
if (env.FACEBOOK_MESSENGER) {
|
||||
facebookMessengerEl.href = env.FACEBOOK_MESSENGER;
|
||||
} else {
|
||||
facebookMessengerEl.nextElementSibling.remove()
|
||||
facebookMessengerEl.remove()
|
||||
}
|
||||
|
||||
|
||||
var linkedInEl = document.getElementById('linkedin');
|
||||
if (env.LINKED_IN) {
|
||||
linkedInEl.href = env.LINKED_IN;
|
||||
} else {
|
||||
linkedInEl.nextElementSibling.remove()
|
||||
linkedInEl.remove()
|
||||
}
|
||||
|
||||
|
||||
var youTubeEl = document.getElementById('youtube');
|
||||
if (env.YOUTUBE) {
|
||||
youTubeEl.href = env.YOUTUBE;
|
||||
} else {
|
||||
youTubeEl.nextElementSibling.remove()
|
||||
youTubeEl.remove()
|
||||
}
|
||||
|
||||
|
||||
var discordEl = document.getElementById('discord');
|
||||
if (env.DISCORD) {
|
||||
discordEl.href = env.DISCORD;
|
||||
} else {
|
||||
discordEl.nextElementSibling.remove()
|
||||
discordEl.remove()
|
||||
}
|
||||
|
||||
|
||||
var twitchEl = document.getElementById('twitch');
|
||||
if (env.TWITCH) {
|
||||
twitchEl.href = env.TWITCH;
|
||||
} else {
|
||||
twitchEl.nextElementSibling.remove()
|
||||
twitchEl.remove()
|
||||
}
|
||||
|
||||
var productHunEl = document.getElementById('producthunt');
|
||||
if (env.PRODUCT_HUNT) {
|
||||
productHunEl.href = env.PRODUCT_HUNT;
|
||||
} else {
|
||||
productHunEl.nextElementSibling.remove()
|
||||
productHunEl.remove()
|
||||
}
|
||||
|
||||
var snapchatEl = document.getElementById('snapchat');
|
||||
if (env.SNAPCHAT) {
|
||||
snapchatEl.href = env.SNAPCHAT;
|
||||
} else {
|
||||
snapchatEl.nextElementSibling.remove()
|
||||
snapchatEl.remove()
|
||||
}
|
||||
|
||||
var spotifyEl = document.getElementById('spotify');
|
||||
if (env.SPOTIFY) {
|
||||
spotifyEl.href = env.SPOTIFY;
|
||||
} else {
|
||||
spotifyEl.nextElementSibling.remove()
|
||||
spotifyEl.remove()
|
||||
}
|
||||
|
||||
var redditEl = document.getElementById('reddit');
|
||||
if (env.REDDIT) {
|
||||
redditEl.href = env.REDDIT;
|
||||
} else {
|
||||
redditEl.nextElementSibling.remove()
|
||||
redditEl.remove()
|
||||
}
|
||||
|
||||
|
||||
var mediumEl = document.getElementById('medium');
|
||||
if (env.MEDIUM) {
|
||||
mediumEl.href = env.MEDIUM;
|
||||
} else {
|
||||
mediumEl.nextElementSibling.remove()
|
||||
mediumEl.remove()
|
||||
}
|
||||
|
||||
|
||||
var pinterestEl = document.getElementById('pinterest');
|
||||
if (env.PINTEREST) {
|
||||
pinterestEl.href = env.PINTEREST;
|
||||
} else {
|
||||
pinterestEl.nextElementSibling.remove()
|
||||
pinterestEl.remove()
|
||||
}
|
||||
|
||||
var tiktokEl = document.getElementById('tiktok');
|
||||
if (env.TIKTOK) {
|
||||
tiktokEl.href = env.TIKTOK;
|
||||
} else {
|
||||
tiktokEl.nextElementSibling.remove()
|
||||
tiktokEl.remove()
|
||||
}
|
||||
|
||||
var emailEl = document.getElementById('email');
|
||||
if (env.EMAIL) {
|
||||
emailEl.innerHTML = env.EMAIL_TEXT || env.EMAIL
|
||||
emailEl.href = 'mailto:' + env.EMAIL;
|
||||
} else {
|
||||
emailEl.nextElementSibling.remove()
|
||||
emailEl.remove()
|
||||
}
|
||||
|
||||
|
||||
var emailAltEl = document.getElementById('email-alt');
|
||||
if (env.EMAIL_ALT) {
|
||||
emailAltEl.innerHTML = env.EMAIL_ALT_TEXT || env.EMAIL_ALT
|
||||
emailAltEl.href = 'mailto:' + env.EMAIL_ALT;
|
||||
} else {
|
||||
emailAltEl.nextElementSibling.remove()
|
||||
emailAltEl.remove()
|
||||
}
|
||||
|
||||
var soundCloudEl = document.getElementById('soundcloud');
|
||||
if (env.SOUND_CLOUD) {
|
||||
soundCloudEl.href = env.SOUND_CLOUD;
|
||||
} else {
|
||||
soundCloudEl.nextElementSibling.remove()
|
||||
soundCloudEl.remove()
|
||||
}
|
||||
|
||||
|
||||
var figmaEl = document.getElementById('figma');
|
||||
if (env.FIGMA) {
|
||||
figmaEl.href = env.FIGMA;
|
||||
} else {
|
||||
figmaEl.nextElementSibling.remove()
|
||||
figmaEl.remove()
|
||||
}
|
||||
|
||||
|
||||
var kitEl = document.getElementById('kit');
|
||||
if (env.KIT) {
|
||||
kitEl.href = env.KIT;
|
||||
} else {
|
||||
kitEl.nextElementSibling.remove()
|
||||
kitEl.remove()
|
||||
}
|
||||
|
||||
|
||||
var telegramEl = document.getElementById('telegram');
|
||||
if (env.TELEGRAM) {
|
||||
telegramEl.href = env.TELEGRAM;
|
||||
} else {
|
||||
telegramEl.nextElementSibling.remove()
|
||||
telegramEl.remove()
|
||||
}
|
||||
|
||||
var temblrEl = document.getElementById('tumblr');
|
||||
if (env.TUMBLR) {
|
||||
temblrEl.href = env.TUMBLR;
|
||||
} else {
|
||||
temblrEl.nextElementSibling.remove()
|
||||
temblrEl.remove()
|
||||
}
|
||||
|
||||
|
||||
var steamEl = document.getElementById('steam');
|
||||
if (env.STEAM) {
|
||||
steamEl.href = env.STEAM;
|
||||
} else {
|
||||
steamEl.nextElementSibling.remove()
|
||||
steamEl.remove()
|
||||
}
|
||||
|
||||
|
||||
var vimeoEl = document.getElementById('vimeo');
|
||||
if (env.VIMEO) {
|
||||
vimeoEl.href = env.VIMEO;
|
||||
} else {
|
||||
vimeoEl.nextElementSibling.remove()
|
||||
vimeoEl.remove()
|
||||
}
|
||||
|
||||
|
||||
var wordpressEl = document.getElementById('wordpress');
|
||||
if (env.WORDPRESS) {
|
||||
wordpressEl.href = env.WORDPRESS;
|
||||
} else {
|
||||
wordpressEl.nextElementSibling.remove()
|
||||
wordpressEl.remove()
|
||||
}
|
||||
|
||||
var goodreadsEl = document.getElementById('goodreads');
|
||||
if (env.GOODREADS) {
|
||||
goodreadsEl.href = env.GOODREADS;
|
||||
} else {
|
||||
goodreadsEl.nextElementSibling.remove()
|
||||
goodreadsEl.remove()
|
||||
}
|
||||
|
||||
var skoobEl = document.getElementById('skoob');
|
||||
if (env.SKOOB) {
|
||||
skoobEl.href = env.SKOOB;
|
||||
} else {
|
||||
skoobEl.nextElementSibling.remove()
|
||||
skoobEl.remove()
|
||||
}
|
||||
|
||||
var whatsappEl = document.getElementById('whatsapp');
|
||||
if (env.WHATSAPP) {
|
||||
whatsappEl.href = env.WHATSAPP;
|
||||
} else {
|
||||
whatsappEl.nextElementSibling.remove()
|
||||
whatsappEl.remove()
|
||||
}
|
||||
|
||||
var letterboxdEl = document.getElementById('letterboxd');
|
||||
if (env.LETTERBOXD) {
|
||||
letterboxdEl.href = env.LETTERBOXD;
|
||||
} else {
|
||||
letterboxdEl.nextElementSibling.remove()
|
||||
letterboxdEl.remove();
|
||||
}
|
||||
|
||||
var footerEl = document.getElementById('footer');
|
||||
if (env.FOOTER) {
|
||||
footerEl.innerHTML = env.FOOTER;
|
||||
} else {
|
||||
footerEl.remove()
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
|
53
package.json
@ -6,33 +6,40 @@
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "node app.js"
|
||||
"start": "razzle start",
|
||||
"build": "razzle build",
|
||||
"debug": "yarn start --inspect --inspect-port 9230",
|
||||
"test": "razzle test --env=jsdom",
|
||||
"start:prod": "NODE_ENV=production node build/server.js",
|
||||
"lint": "eslint ."
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/timothystewart6/littlelink.git"
|
||||
},
|
||||
"keywords": [
|
||||
"littlelink",
|
||||
"linktree",
|
||||
"linktr.ee",
|
||||
"javascript",
|
||||
"open",
|
||||
"source",
|
||||
"html",
|
||||
"css",
|
||||
"docker"
|
||||
],
|
||||
"author": "Timothy Stewart",
|
||||
"bugs": {
|
||||
"url": "https://github.com/timothystewart6/littlelink/issues"
|
||||
},
|
||||
"homepage": "https://github.com/timothystewart6/littlelink#readme",
|
||||
"dependencies": {
|
||||
"compression": "^1.7.4",
|
||||
"express": "^4.17.1",
|
||||
"morgan": "^1.10.0",
|
||||
"jsdom": "^17.0.0"
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"serialize-javascript": "^6.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.15.0",
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"babel-preset-razzle": "4.0.6",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-import": "^2.24.1",
|
||||
"eslint-plugin-jsx": "^0.1.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^3.4.1",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-react": "^7.24.0",
|
||||
"html-webpack-plugin": "^4.5.2",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"prettier": "^2.3.2",
|
||||
"razzle": "^4.0.6",
|
||||
"razzle-dev-utils": "^4.0.6",
|
||||
"webpack": "^4.44.1",
|
||||
"webpack-dev-server": "^3.11.2"
|
||||
}
|
||||
}
|
||||
|
@ -317,11 +317,3 @@ button:hover,
|
||||
.button.button-wordpress:hover,
|
||||
.button.button-wordpress:focus {
|
||||
filter: brightness(90%) }
|
||||
|
||||
/* WhatsApp */
|
||||
.button.button-whatsapp {
|
||||
color: #FFFFFF;
|
||||
background-color: #2DB842 }
|
||||
.button.button-whatsapp:hover,
|
||||
.button.button-whatsapp:focus {
|
||||
filter: brightness(90%) }
|
2
public/robots.txt
Normal file
@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
|
5
sandbox.config.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"container": {
|
||||
"port": 3000
|
||||
}
|
||||
}
|
15
src/client.js
Normal file
@ -0,0 +1,15 @@
|
||||
import App from './components/App/App';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import React from 'react';
|
||||
import { hydrate } from 'react-dom';
|
||||
|
||||
hydrate(
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>,
|
||||
document.getElementById('root'),
|
||||
);
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept();
|
||||
}
|
13
src/components/App/App.js
Normal file
@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import Home from '../Home/Home';
|
||||
|
||||
const App = () => (
|
||||
<div className="container">
|
||||
<Switch>
|
||||
<Route exact path="/" component={Home} />
|
||||
</Switch>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default App;
|
16
src/components/App/App.test.js
Normal file
@ -0,0 +1,16 @@
|
||||
import App from './App';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
describe('<App />', () => {
|
||||
test('renders without exploding', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(
|
||||
<MemoryRouter>
|
||||
<App />
|
||||
</MemoryRouter>,
|
||||
div,
|
||||
);
|
||||
});
|
||||
});
|
6
src/components/Avatar/Avatar.css
Normal file
@ -0,0 +1,6 @@
|
||||
.avatar {
|
||||
vertical-align: middle;
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
border-radius: 50%;
|
||||
}
|
20
src/components/Avatar/Avatar.js
Normal file
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { string } from 'prop-types';
|
||||
|
||||
import './Avatar.css';
|
||||
|
||||
function Avatar(props) {
|
||||
const { src, srcSet, alt } = props;
|
||||
|
||||
return (
|
||||
<img id="avatar" className="avatar" src={src} srcSet={srcSet} alt={alt} />
|
||||
);
|
||||
}
|
||||
|
||||
export default Avatar;
|
||||
|
||||
Avatar.propType = {
|
||||
src: string,
|
||||
srcSet: string,
|
||||
alt: string,
|
||||
};
|
33
src/components/Button/Button.js
Normal file
@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import { string } from 'prop-types';
|
||||
|
||||
function Button(props) {
|
||||
const { name, href, displayName, logo } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<a
|
||||
id={name}
|
||||
className={`button button-${name}`}
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{logo && (
|
||||
<img className="icon" src={logo} alt={`${displayName} logo`} />
|
||||
)}
|
||||
|
||||
{displayName}
|
||||
</a>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Button;
|
||||
|
||||
Button.propType = {
|
||||
src: string.isRequired,
|
||||
srcSet: string,
|
||||
alt: string.isRequired,
|
||||
href: string,
|
||||
};
|
10
src/components/Button/Button.test.js
Normal file
@ -0,0 +1,10 @@
|
||||
import Button from './Button';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
describe('<Button />', () => {
|
||||
test('renders without exploding', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<Button />, div);
|
||||
});
|
||||
});
|
288
src/components/Home/Home.js
Normal file
@ -0,0 +1,288 @@
|
||||
import React from 'react';
|
||||
import Avatar from '../Avatar/Avatar';
|
||||
import Button from '../Button/Button';
|
||||
import { runtimeConfig } from '../../config';
|
||||
import githubLogo from '../../icons/github.svg';
|
||||
import instagramLogo from '../../icons/instagram.svg';
|
||||
import kitLogo from '../../icons/kit.svg';
|
||||
import tiktokLogo from '../../icons/tiktok.svg';
|
||||
import twitchLogo from '../../icons/twitch.svg';
|
||||
import twitterLogo from '../../icons/twitter.svg';
|
||||
import youtubeLogo from '../../icons/youtube.svg';
|
||||
import facebookLogo from '../../icons/facebook.svg';
|
||||
import messengerLogo from '../../icons/messenger.svg';
|
||||
import linkedinLogo from '../../icons/linkedin.svg';
|
||||
import producthuntLogo from '../../icons/producthunt.svg';
|
||||
import snapchatLogo from '../../icons/snapchat.svg';
|
||||
import spotifyLogo from '../../icons/spotify.svg';
|
||||
import redditLogo from '../../icons/reddit.svg';
|
||||
import mediumLogo from '../../icons/medium.svg';
|
||||
import pinterestLogo from '../../icons/pinterest.svg';
|
||||
import soundcloudLogo from '../../icons/soundcloud.svg';
|
||||
import figmaLogo from '../../icons/figma.svg';
|
||||
import telegramLogo from '../../icons/telegram.svg';
|
||||
import tumblrLogo from '../../icons/tumblr.svg';
|
||||
import steamLogo from '../../icons/steam.svg';
|
||||
import vimeoLogo from '../../icons/vimeo.svg';
|
||||
import wordpressLogo from '../../icons/wordpress.svg';
|
||||
import goodreadsLogo from '../../icons/goodreads.svg';
|
||||
import skoobLogo from '../../icons/skoob.svg';
|
||||
import letterboxdLogo from '../../icons/letterboxd.svg';
|
||||
import mastodonLogo from '../../icons/mastodon.svg';
|
||||
|
||||
function Home(props) {
|
||||
return (
|
||||
<>
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
<div className="column" style={{ marginTop: '10%' }}>
|
||||
<Avatar
|
||||
src={runtimeConfig.AVATAR_URL}
|
||||
srcSet={runtimeConfig.AVATAR_2X_URL}
|
||||
alt={runtimeConfig.AVATAR_ALT}
|
||||
/>
|
||||
<h1 id="name">{`${runtimeConfig.NAME}`}</h1>
|
||||
<p id="bio">{runtimeConfig.BIO}</p>
|
||||
{runtimeConfig.GITHUB && (
|
||||
<Button
|
||||
name="github"
|
||||
href={runtimeConfig.GITHUB}
|
||||
displayName="GitHub"
|
||||
logo={githubLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.TWITTER && (
|
||||
<Button
|
||||
name="twitter"
|
||||
href={runtimeConfig.TWITTER}
|
||||
displayName="Twitter"
|
||||
logo={twitterLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.INSTAGRAM && (
|
||||
<Button
|
||||
name="instagram"
|
||||
href={runtimeConfig.INSTAGRAM}
|
||||
displayName="Instagram"
|
||||
logo={instagramLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.YOUTUBE && (
|
||||
<Button
|
||||
name="youtube"
|
||||
href={runtimeConfig.YOUTUBE}
|
||||
displayName="YouTube"
|
||||
logo={youtubeLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.TWITCH && (
|
||||
<Button
|
||||
name="twitch"
|
||||
href={runtimeConfig.TWITCH}
|
||||
displayName="Twitch"
|
||||
logo={twitchLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.TIKTOK && (
|
||||
<Button
|
||||
name="tiktok"
|
||||
href={runtimeConfig.TIKTOK}
|
||||
displayName="TikTok"
|
||||
logo={tiktokLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.KIT && (
|
||||
<Button
|
||||
name="kit"
|
||||
href={runtimeConfig.KIT}
|
||||
displayName="Kit"
|
||||
logo={kitLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.FACEBOOK && (
|
||||
<Button
|
||||
name="facebook"
|
||||
href={runtimeConfig.FACEBOOK}
|
||||
displayName="Facebook"
|
||||
logo={facebookLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.FACEBOOK_MESSENGER && (
|
||||
<Button
|
||||
name="messenger"
|
||||
href={runtimeConfig.FACEBOOK_MESSENGER}
|
||||
displayName="Messenger"
|
||||
logo={messengerLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.LINKED_IN && (
|
||||
<Button
|
||||
name="linkedin"
|
||||
href={runtimeConfig.LINKED_IN}
|
||||
displayName="LinkedIn"
|
||||
logo={linkedinLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.PRODUCT_HUNT && (
|
||||
<Button
|
||||
name="producthunt"
|
||||
href={runtimeConfig.PRODUCT_HUNT}
|
||||
displayName="Product Hunt"
|
||||
logo={producthuntLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.SNAPCHAT && (
|
||||
<Button
|
||||
name="snapchat"
|
||||
href={runtimeConfig.SNAPCHAT}
|
||||
displayName="SnapChat"
|
||||
logo={snapchatLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.SPOTIFY && (
|
||||
<Button
|
||||
name="spotify"
|
||||
href={runtimeConfig.SPOTIFY}
|
||||
displayName="Spotify"
|
||||
logo={spotifyLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.REDDIT && (
|
||||
<Button
|
||||
name="reddit"
|
||||
href={runtimeConfig.REDDIT}
|
||||
displayName="Reddit"
|
||||
logo={redditLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.MEDIUM && (
|
||||
<Button
|
||||
name="medium"
|
||||
href={runtimeConfig.MEDIUM}
|
||||
displayName="Medium"
|
||||
logo={mediumLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.PINTEREST && (
|
||||
<Button
|
||||
name="pinterest"
|
||||
href={runtimeConfig.PINTEREST}
|
||||
displayName="Pinterest"
|
||||
logo={pinterestLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.EMAIL && (
|
||||
<Button
|
||||
name="default"
|
||||
href={`mailto:${runtimeConfig.EMAIL}`}
|
||||
displayName={runtimeConfig.EMAIL_TEXT}
|
||||
/>
|
||||
)}
|
||||
|
||||
{runtimeConfig.EMAIL_ALT && (
|
||||
<Button
|
||||
name="default"
|
||||
href={`mailto:${runtimeConfig.EMAIL_ALT_TEXT}`}
|
||||
displayName={runtimeConfig.EMAIL_ALT_TEXT}
|
||||
/>
|
||||
)}
|
||||
|
||||
{runtimeConfig.SOUND_CLOUD && (
|
||||
<Button
|
||||
name="soundcloud"
|
||||
href={runtimeConfig.SOUND_CLOUD}
|
||||
displayName="SoundCloud"
|
||||
logo={soundcloudLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.FIGMA && (
|
||||
<Button
|
||||
name="figma"
|
||||
href={runtimeConfig.FIGMA}
|
||||
displayName="Figma"
|
||||
logo={figmaLogo}
|
||||
/>
|
||||
)}
|
||||
|
||||
{runtimeConfig.TELEGRAM && (
|
||||
<Button
|
||||
name="telegram"
|
||||
href={runtimeConfig.TELEGRAM}
|
||||
displayName="Telegram"
|
||||
logo={telegramLogo}
|
||||
/>
|
||||
)}
|
||||
|
||||
{runtimeConfig.TUMBLR && (
|
||||
<Button
|
||||
name="tumblr"
|
||||
href={runtimeConfig.TUMBLR}
|
||||
displayName="Tumblr"
|
||||
logo={tumblrLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.STEAM && (
|
||||
<Button
|
||||
name="steam"
|
||||
href={runtimeConfig.STEAM}
|
||||
displayName="Steam"
|
||||
logo={steamLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.VIMEO && (
|
||||
<Button
|
||||
name="vimeo"
|
||||
href={runtimeConfig.VIMEO}
|
||||
displayName="Vimeo"
|
||||
logo={vimeoLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.WORDPRESS && (
|
||||
<Button
|
||||
name="wordpress"
|
||||
href={runtimeConfig.WORDPRESS}
|
||||
displayName="Wordpress"
|
||||
logo={wordpressLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.GOODREADS && (
|
||||
<Button
|
||||
name="goodreads"
|
||||
href={runtimeConfig.GOODREADS}
|
||||
displayName="Goodreads"
|
||||
logo={goodreadsLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.SKOOB && (
|
||||
<Button
|
||||
name="skoob"
|
||||
href={runtimeConfig.SKOOB}
|
||||
displayName="Skoob"
|
||||
logo={skoobLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.LETTERBOXD && (
|
||||
<Button
|
||||
name="letterboxd"
|
||||
href={runtimeConfig.LETTERBOXD}
|
||||
displayName="LetterBoxd"
|
||||
logo={letterboxdLogo}
|
||||
/>
|
||||
)}
|
||||
{runtimeConfig.MASTODON && (
|
||||
<Button
|
||||
name="mastodon"
|
||||
href={runtimeConfig.MASTODON}
|
||||
displayName="Mastodon"
|
||||
logo={mastodonLogo}
|
||||
/>
|
||||
)}
|
||||
<p id="footer">{runtimeConfig.FOOTER}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Home;
|
10
src/components/Home/Home.test.js
Normal file
@ -0,0 +1,10 @@
|
||||
import Home from './Home';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
describe('<Home />', () => {
|
||||
test('renders without exploding', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<Home />, div);
|
||||
});
|
||||
});
|
162
src/config.js
Normal file
@ -0,0 +1,162 @@
|
||||
// config.js
|
||||
const nodeIsProduction = process.env.NODE_ENV === 'production';
|
||||
export const runtimeConfig =
|
||||
typeof window !== 'undefined'
|
||||
? {
|
||||
// client
|
||||
META_TITLE: window?.env?.META_TITLE,
|
||||
META_DESCRIPTION: window?.env?.META_DESCRIPTION,
|
||||
META_AUTHOR: window?.env?.META_AUTHOR,
|
||||
THEME: window?.env?.THEME,
|
||||
FAVICON_URL: window?.env?.FAVICON_URL,
|
||||
AVATAR_URL: window?.env?.AVATAR_URL,
|
||||
AVATAR_ALT: window?.env?.AVATAR_ALT,
|
||||
AVATAR_2X_URL: window?.env?.AVATAR_2X_URL,
|
||||
NAME: window?.env?.NAME,
|
||||
BIO: window?.env?.BIO,
|
||||
GITHUB: window?.env?.GITHUB,
|
||||
TWITTER: window?.env?.TWITTER,
|
||||
MICRO_BLOG: window?.env?.MICRO_BLOG,
|
||||
INSTAGRAM: window?.env?.INSTAGRAM,
|
||||
FACEBOOK: window?.env?.FACEBOOK,
|
||||
FACEBOOK_MESSENGER: window?.env?.FACEBOOK_MESSENGER,
|
||||
LINKED_IN: window?.env?.LINKED_IN,
|
||||
YOUTUBE: window?.env?.YOUTUBE,
|
||||
DISCORD: window?.env?.DISCORD,
|
||||
TWITCH: window?.env?.TWITCH,
|
||||
PRODUCT_HUNT: window?.env?.PRODUCT_HUNT,
|
||||
SNAPCHAT: window?.env?.SNAPCHAT,
|
||||
SPOTIFY: window?.env?.SPOTIFY,
|
||||
REDDIT: window?.env?.REDDIT,
|
||||
MEDIUM: window?.env?.MEDIUM,
|
||||
PINTEREST: window?.env?.PINTEREST,
|
||||
TIKTOK: window?.env?.TIKTOK,
|
||||
EMAIL: window?.env?.EMAIL,
|
||||
EMAIL_TEXT: window?.env?.EMAIL_TEXT,
|
||||
EMAIL_ALT: window?.env?.EMAIL_ALT,
|
||||
EMAIL_ALT_TEXT: window?.env?.EMAIL_ALT_TEXT,
|
||||
SOUND_CLOUD: window?.env?.SOUND_CLOUD,
|
||||
FIGMA: window?.env?.FIGMA,
|
||||
KIT: window?.env?.KIT,
|
||||
TELEGRAM: window?.env?.TELEGRAM,
|
||||
TUMBLR: window?.env?.TUMBLR,
|
||||
STEAM: window?.env?.STEAM,
|
||||
VIMEO: window?.env?.VIMEO,
|
||||
WORDPRESS: window?.env?.WORDPRESS,
|
||||
GOODREADS: window?.env?.GOODREADS,
|
||||
SKOOB: window?.env?.SKOOB,
|
||||
LETTERBOXD: window?.env?.LETTERBOXD,
|
||||
MASTODON: window?.env?.MASTODON,
|
||||
FOOTER: window?.env?.FOOTER,
|
||||
}
|
||||
: {
|
||||
// server
|
||||
META_TITLE: nodeIsProduction
|
||||
? process.env.META_TITLE
|
||||
: process.env.RAZZLE_META_TITLE,
|
||||
META_DESCRIPTION: nodeIsProduction
|
||||
? process.env.META_DESCRIPTION
|
||||
: process.env.RAZZLE_META_DESCRIPTION,
|
||||
META_AUTHOR: nodeIsProduction
|
||||
? process.env.META_AUTHOR
|
||||
: process.env.RAZZLE_META_AUTHOR,
|
||||
THEME: nodeIsProduction ? process.env.THEME : process.env.RAZZLE_THEME,
|
||||
FAVICON_URL: nodeIsProduction
|
||||
? process.env.FAVICON_URL
|
||||
: process.env.RAZZLE_FAVICON_URL,
|
||||
AVATAR_URL: nodeIsProduction
|
||||
? process.env.AVATAR_URL
|
||||
: process.env.RAZZLE_AVATAR_URL,
|
||||
AVATAR_ALT: nodeIsProduction
|
||||
? process.env.AVATAR_ALT
|
||||
: process.env.RAZZLE_AVATAR_ALT,
|
||||
AVATAR_2X_URL: nodeIsProduction
|
||||
? process.env.AVATAR_2X_URL
|
||||
: process.env.RAZZLE_AVATAR_2X_URL,
|
||||
NAME: nodeIsProduction ? process.env.NAME : process.env.RAZZLE_NAME,
|
||||
BIO: nodeIsProduction ? process.env.BIO : process.env.RAZZLE_BIO,
|
||||
GITHUB: nodeIsProduction
|
||||
? process.env.GITHUB
|
||||
: process.env.RAZZLE_GITHUB,
|
||||
TWITTER: nodeIsProduction
|
||||
? process.env.GITHUB
|
||||
: process.env.RAZZLE_TWITTER,
|
||||
INSTAGRAM: nodeIsProduction
|
||||
? process.env.INSTAGRAM
|
||||
: process.env.RAZZLE_INSTAGRAM,
|
||||
YOUTUBE: nodeIsProduction
|
||||
? process.env.YOUTUBE
|
||||
: process.env.RAZZLE_YOUTUBE,
|
||||
TWITCH: nodeIsProduction
|
||||
? process.env.TWITCH
|
||||
: process.env.RAZZLE_TWITCH,
|
||||
TIKTOK: nodeIsProduction
|
||||
? process.env.TIKTOK
|
||||
: process.env.RAZZLE_TIKTOK,
|
||||
KIT: nodeIsProduction ? process.env.KIT : process.env.RAZZLE_KIT,
|
||||
FACEBOOK: nodeIsProduction
|
||||
? process.env.FACEBOOK
|
||||
: process.env.RAZZLE_FACEBOOK,
|
||||
FACEBOOK_MESSENGER: nodeIsProduction
|
||||
? process.env.FACEBOOK_MESSENGER
|
||||
: process.env.RAZZLE_FACEBOOK_MESSENGER,
|
||||
LINKED_IN: nodeIsProduction
|
||||
? process.env.LINKED_IN
|
||||
: process.env.RAZZLE_LINKED_IN,
|
||||
PRODUCT_HUNT: nodeIsProduction
|
||||
? process.env.PRODUCT_HUNT
|
||||
: process.env.RAZZLE_PRODUCT_HUNT,
|
||||
SNAPCHAT: nodeIsProduction
|
||||
? process.env.SNAPCHAT
|
||||
: process.env.RAZZLE_SNAPCHAT,
|
||||
SPOTIFY: nodeIsProduction
|
||||
? process.env.SPOTIFY
|
||||
: process.env.RAZZLE_SPOTIFY,
|
||||
REDDIT: nodeIsProduction
|
||||
? process.env.REDDIT
|
||||
: process.env.RAZZLE_REDDIT,
|
||||
MEDIUM: nodeIsProduction
|
||||
? process.env.MEDIUM
|
||||
: process.env.RAZZLE_MEDIUM,
|
||||
PINTEREST: nodeIsProduction
|
||||
? process.env.PINTEREST
|
||||
: process.env.RAZZLE_PINTEREST,
|
||||
EMAIL: nodeIsProduction ? process.env.EMAIL : process.env.RAZZLE_EMAIL,
|
||||
EMAIL_TEXT: nodeIsProduction
|
||||
? process.env.EMAIL_TEXT
|
||||
: process.env.RAZZLE_EMAIL_TEXT,
|
||||
EMAIL_ALT: nodeIsProduction
|
||||
? process.env.EMAIL_ALT
|
||||
: process.env.RAZZLE_EMAIL_ALT,
|
||||
EMAIL_ALT_TEXT: nodeIsProduction
|
||||
? process.env.EMAIL_ALT_TEXT
|
||||
: process.env.RAZZLE_EMAIL_ALT_TEXT,
|
||||
SOUND_CLOUD: nodeIsProduction
|
||||
? process.env.SOUND_CLOUD
|
||||
: process.env.RAZZLE_SOUND_CLOUD,
|
||||
FIGMA: nodeIsProduction ? process.env.FIGMA : process.env.RAZZLE_FIGMA,
|
||||
TELEGRAM: nodeIsProduction
|
||||
? process.env.TELEGRAM
|
||||
: process.env.RAZZLE_TELEGRAM,
|
||||
TUMBLR: nodeIsProduction
|
||||
? process.env.TUMBLR
|
||||
: process.env.RAZZLE_TUMBLR,
|
||||
STEAM: nodeIsProduction ? process.env.STEAM : process.env.RAZZLE_STEAM,
|
||||
VIMEO: nodeIsProduction ? process.env.VIMEO : process.env.RAZZLE_VIMEO,
|
||||
WORDPRESS: nodeIsProduction
|
||||
? process.env.WORDPRESS
|
||||
: process.env.RAZZLE_WORDPRESS,
|
||||
GOODREADS: nodeIsProduction
|
||||
? process.env.GOODREADS
|
||||
: process.env.RAZZLE_GOODREADS,
|
||||
SKOOB: nodeIsProduction ? process.env.SKOOB : process.env.RAZZLE_SKOOB,
|
||||
LETTERBOXD: nodeIsProduction
|
||||
? process.env.LETTERBOXD
|
||||
: process.env.RAZZLE_LETTERBOXD,
|
||||
MASTODON: nodeIsProduction
|
||||
? process.env.MICRO_BLOG
|
||||
: process.env.RAZZLE_MICRO_BLOG,
|
||||
FOOTER: nodeIsProduction
|
||||
? process.env.FOOTER
|
||||
: process.env.RAZZLE_FOOTER,
|
||||
};
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 314 B After Width: | Height: | Size: 314 B |
Before Width: | Height: | Size: 677 B After Width: | Height: | Size: 677 B |
Before Width: | Height: | Size: 482 B After Width: | Height: | Size: 482 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 642 B After Width: | Height: | Size: 642 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 736 B After Width: | Height: | Size: 736 B |
Before Width: | Height: | Size: 907 B After Width: | Height: | Size: 907 B |
Before Width: | Height: | Size: 722 B After Width: | Height: | Size: 722 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 729 B After Width: | Height: | Size: 729 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 1005 B After Width: | Height: | Size: 1005 B |
Before Width: | Height: | Size: 944 B After Width: | Height: | Size: 944 B |
Before Width: | Height: | Size: 458 B After Width: | Height: | Size: 458 B |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 579 B After Width: | Height: | Size: 579 B |
Before Width: | Height: | Size: 454 B After Width: | Height: | Size: 454 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 816 B After Width: | Height: | Size: 816 B |
28
src/index.js
Normal file
@ -0,0 +1,28 @@
|
||||
/* eslint-disable no-console */
|
||||
import express from 'express';
|
||||
|
||||
let app = require('./server').default;
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept('./server', () => {
|
||||
console.log('🔁 HMR Reloading `./server`...');
|
||||
try {
|
||||
app = require('./server').default;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
console.info('✅ Server-side HMR Enabled!');
|
||||
}
|
||||
|
||||
const port = process.env.PORT || 3000;
|
||||
|
||||
export default express()
|
||||
.use((req, res) => app.handle(req, res))
|
||||
.listen(port, err => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
console.log(`> Started on port ${port}`);
|
||||
});
|
92
src/server.js
Normal file
@ -0,0 +1,92 @@
|
||||
import App from './components/App/App';
|
||||
import React from 'react';
|
||||
import { StaticRouter } from 'react-router-dom';
|
||||
import express from 'express';
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import { runtimeConfig } from './config';
|
||||
import serialize from 'serialize-javascript'; // Safer stringify, prevents XSS attacks
|
||||
import morgan from 'morgan';
|
||||
import compression from 'compression';
|
||||
|
||||
const assets = require(process.env.RAZZLE_ASSETS_MANIFEST);
|
||||
|
||||
const cssLinksFromAssets = (assets, entrypoint) => {
|
||||
return assets[entrypoint]
|
||||
? assets[entrypoint].css
|
||||
? assets[entrypoint].css
|
||||
.map(asset => `<link rel="stylesheet" href="${asset}">`)
|
||||
.join('')
|
||||
: ''
|
||||
: '';
|
||||
};
|
||||
|
||||
const jsScriptTagsFromAssets = (assets, entrypoint, extra = '') => {
|
||||
return assets[entrypoint]
|
||||
? assets[entrypoint].js
|
||||
? assets[entrypoint].js
|
||||
.map(asset => `<script src="${asset}"${extra}></script>`)
|
||||
.join('')
|
||||
: ''
|
||||
: '';
|
||||
};
|
||||
|
||||
const theme = runtimeConfig.THEME === 'Dark' ? 'dark.css' : 'light.css';
|
||||
|
||||
const server = express();
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
server.use(morgan('combined'));
|
||||
server.use(compression());
|
||||
}
|
||||
|
||||
server
|
||||
.disable('x-powered-by')
|
||||
.use(express.static(process.env.RAZZLE_PUBLIC_DIR))
|
||||
.get('/*', (req, res) => {
|
||||
const context = {};
|
||||
const markup = renderToString(
|
||||
<StaticRouter context={context} location={req.url}>
|
||||
<App />
|
||||
</StaticRouter>,
|
||||
);
|
||||
|
||||
if (context.url) {
|
||||
res.redirect(context.url);
|
||||
} else {
|
||||
res.status(200).send(
|
||||
`<!doctype html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta charset="utf-8" />
|
||||
<title id="meta-title">${runtimeConfig.META_TITLE}</title>
|
||||
<meta id="meta-description" name="description" content="${
|
||||
runtimeConfig.META_DESCRIPTION
|
||||
}">
|
||||
<meta id="meta-author" name="author" content="${
|
||||
runtimeConfig.META_AUTHOR
|
||||
}">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,600,700,800&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="css/normalize.css">
|
||||
<link id="theme" rel="stylesheet" href="css/${theme}">
|
||||
<link rel="stylesheet" href="css/brands.css">
|
||||
${cssLinksFromAssets(assets, 'client')}
|
||||
<link id="favicon" rel="icon" type="image/png" href="${
|
||||
runtimeConfig.FAVICON_URL
|
||||
}">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">${markup}</div>
|
||||
<script>window.env = ${serialize(runtimeConfig)};</script>
|
||||
${jsScriptTagsFromAssets(assets, 'client', ' defer crossorigin')}
|
||||
</body>
|
||||
</html>`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default server;
|
||||
|
||||
// ${jsScriptTagsFromAssets(assets, 'client', ' defer crossorigin')}
|
@ -1,172 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title id="meta-title">LittleLink</title>
|
||||
<meta id="meta-description" name="description" content="Find us online!">
|
||||
<meta id="meta-author" name="author" content="Seth Cottle">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,600,700,800&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="css/normalize.css">
|
||||
<link id="theme" rel="stylesheet" href="css/skeleton-light.css">
|
||||
<link rel="stylesheet" href="css/brands.css">
|
||||
<link id="favicon" rel="icon" type="image/png" href="images/avatar.png">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="column" style="margin-top: 10%">
|
||||
|
||||
<img id="avatar" class="avatar" src="images/avatar.png" srcset="images/avatar@2x.png 2x"
|
||||
alt="LittleLink Logo">
|
||||
|
||||
<h1 id="name">LittleLink</h1>
|
||||
|
||||
<p id="bio">LittleLink is an open source DIY alternative to services like <a href="https://linktr.ee"
|
||||
target="_blank" rel="noopener">Linktree</a> and <a href="https://many.link" target="_blank"
|
||||
rel="noopener">many.link</a>. LittleLink was built using <a href="http://www.getskeleton.com"
|
||||
target="_blank" rel="noopener">Skeleton</a>, a dead simple, responsive boilerplate—we’ve just
|
||||
created some
|
||||
branded buttons and stripped out the things you won't need. 😊</p>
|
||||
|
||||
<a id="github" class="button button-github" href="#" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/github.svg" alt="GitHub Logo">GitHub</a>
|
||||
<br>
|
||||
|
||||
<a id="twitter" class="button button-twitter" href="#" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/twitter.svg" alt="Twitter Logo">Twitter</a>
|
||||
<br>
|
||||
|
||||
<a id="mastodon" class="button button-mastodon" href="#" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/mastodon.svg" alt="Mastodon Logo">Mastodon</a>
|
||||
<br>
|
||||
|
||||
<a id="microblog" class="button button-microblog" href="#" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/microblog.svg" alt="Micro.blog Logo" />Micro.blog</a>
|
||||
<br>
|
||||
|
||||
<a id="instagram" class="button button-instagram" href="#" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/instagram.svg" alt="Instagram Logo">Instagram</a>
|
||||
<br>
|
||||
|
||||
<a id="letterboxd" class="button button-letterboxd" href="#" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/letterboxd.svg" alt="Letterboxd Logo">Letterboxd</a>
|
||||
<br>
|
||||
|
||||
<a id="facebook" class="button button-facebook" href="#" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/facebook.svg" alt="Facebook Logo">Find us on Facebook</a>
|
||||
<br>
|
||||
|
||||
<a id="facebook-messenger" class="button button-messenger" href="#" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/messenger.svg" alt="Facebook Messenger Logo">Chat on Messenger</a>
|
||||
<br>
|
||||
|
||||
<a id="linkedin" class="button button-linkedin" href="#" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/linkedin.svg" alt="LinkedIn Logo">LinkedIn</a>
|
||||
<br>
|
||||
|
||||
<a id="youtube" class="button button-youtube" href="#" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/youtube.svg" alt="YouTube Logo">YouTube</a>
|
||||
<br>
|
||||
|
||||
<a id="discord" class="button button-discord" href="#" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/discord.svg" alt="Discord Logo">Discord</a>
|
||||
<br>
|
||||
|
||||
<a id="twitch" class="button button-twitch" href="#" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/twitch.svg" alt="Twitch Logo">Twitch</a>
|
||||
<br>
|
||||
|
||||
<a id="producthunt" class="button button-producthunt" href="#" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/producthunt.svg" alt="Product Hunt Logo">Product Hunt</a>
|
||||
<br>
|
||||
|
||||
<a id="snapchat" class="button button-snapchat" href="#" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/snapchat.svg" alt="Snapchat Logo">Snapchat</a>
|
||||
<br>
|
||||
|
||||
<a id="spotify" class="button button-spotify" href="#" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/spotify.svg" alt="Spotify Logo">Spotify</a>
|
||||
<br>
|
||||
|
||||
<a id="reddit" class="button button-reddit" href="#" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/reddit.svg" alt="Reddit Logo">Reddit</a>
|
||||
<br>
|
||||
|
||||
<a id="medium" class="button button-medium" href="#" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/medium.svg" alt="Medium Logo">Medium</a>
|
||||
<br>
|
||||
|
||||
<a id="pinterest" class="button button-pinterest" href="#" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/pinterest.svg" alt="Pinterest Logo">Follow on Pinterest</a>
|
||||
<br>
|
||||
|
||||
<a id="tiktok" class="button button-tiktok" href="#" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/tiktok.svg" alt="TikTok Logo">TikTok</a>
|
||||
<br>
|
||||
|
||||
<a id="email" class="button button-default" href="#" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/email.svg" alt="Email Icon">hello@littlelink.io</a>
|
||||
<br>
|
||||
|
||||
<a id="email-alt" class="button button-default" href="#" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/email_alt.svg" alt="Email Icon">hello@littlelink.io</a>
|
||||
<br>
|
||||
|
||||
<a id="soundcloud" class="button button-soundcloud" href="#" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/soundcloud.svg" alt="SoundCloud Logo">SoundCloud</a>
|
||||
<br>
|
||||
|
||||
<a id="figma" class="button button-figma" href="#" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/figma.svg" alt="Figma Logo">Figma</a>
|
||||
<br>
|
||||
|
||||
<a id="kit" class="button button-kit" href="#" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/kit.svg" alt="Kit Logo">Kit</a>
|
||||
<br>
|
||||
|
||||
<a id="telegram" class="button button-telegram" href="#" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/telegram.svg" alt="Telegram Logo">Telegram</a>
|
||||
<br>
|
||||
|
||||
<a id="tumblr" class="button button-tumblr" href="#" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/tumblr.svg" alt="Tumblr Logo">Tumblr</a>
|
||||
<br>
|
||||
|
||||
<a id="steam" class="button button-steam" href="#" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/steam.svg" alt="Steam Logo">Steam</a>
|
||||
<br>
|
||||
|
||||
<a id="vimeo" class="button button-vimeo" href="#" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/vimeo.svg" alt="Vimeo Logo">Vimeo</a>
|
||||
<br>
|
||||
|
||||
<a id="wordpress" class="button button-wordpress" href="#" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/wordpress.svg" alt="Wordpress Logo">Wordpress</a>
|
||||
<br>
|
||||
|
||||
<a id="goodreads" class="button button-goodreads" href="#" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/goodreads.svg" alt="Goodreads Logo">Goodreads</a>
|
||||
<br>
|
||||
|
||||
<a id="skoob" class="button button-skoob" href="#" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/skoob.svg" alt="Skoob Logo">Skoob</a>
|
||||
<br>
|
||||
|
||||
<a id="whatsapp" class="button button-whatsapp" href="$WHATSAPP" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/whatsapp.svg" alt="WhatsApp Logo">WhatsApp</a>
|
||||
<br>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<p id="footer">Build your own by forking <a href="https://littlelink.io" target="_blank"
|
||||
rel="noopener">LittleLink</a>.</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="2489" height="2500" viewBox="0 0 1219.547 1225.016"><path fill="#E0E0E0" d="M1041.858 178.02C927.206 63.289 774.753.07 612.325 0 277.617 0 5.232 272.298 5.098 606.991c-.039 106.986 27.915 211.42 81.048 303.476L0 1225.016l321.898-84.406c88.689 48.368 188.547 73.855 290.166 73.896h.258.003c334.654 0 607.08-272.346 607.222-607.023.056-162.208-63.052-314.724-177.689-429.463zm-429.533 933.963h-.197c-90.578-.048-179.402-24.366-256.878-70.339l-18.438-10.93-191.021 50.083 51-186.176-12.013-19.087c-50.525-80.336-77.198-173.175-77.16-268.504.111-278.186 226.507-504.503 504.898-504.503 134.812.056 261.519 52.604 356.814 147.965 95.289 95.36 147.728 222.128 147.688 356.948-.118 278.195-226.522 504.543-504.693 504.543z"/><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="609.77" y1="1190.114" x2="609.77" y2="21.084"><stop offset="0" stop-color="#20b038"/><stop offset="1" stop-color="#60d66a"/></linearGradient><path fill="url(#a)" d="M27.875 1190.114l82.211-300.18c-50.719-87.852-77.391-187.523-77.359-289.602.133-319.398 260.078-579.25 579.469-579.25 155.016.07 300.508 60.398 409.898 169.891 109.414 109.492 169.633 255.031 169.57 409.812-.133 319.406-260.094 579.281-579.445 579.281-.023 0 .016 0 0 0h-.258c-96.977-.031-192.266-24.375-276.898-70.5l-307.188 80.548z"/><image overflow="visible" opacity=".08" width="682" height="639" xlink:href="FCC0802E2AF8A915.png" transform="translate(270.984 291.372)"/><path fill-rule="evenodd" clip-rule="evenodd" fill="#FFF" d="M462.273 349.294c-11.234-24.977-23.062-25.477-33.75-25.914-8.742-.375-18.75-.352-28.742-.352-10 0-26.25 3.758-39.992 18.766-13.75 15.008-52.5 51.289-52.5 125.078 0 73.797 53.75 145.102 61.242 155.117 7.5 10 103.758 166.266 256.203 226.383 126.695 49.961 152.477 40.023 179.977 37.523s88.734-36.273 101.234-71.297c12.5-35.016 12.5-65.031 8.75-71.305-3.75-6.25-13.75-10-28.75-17.5s-88.734-43.789-102.484-48.789-23.75-7.5-33.75 7.516c-10 15-38.727 48.773-47.477 58.773-8.75 10.023-17.5 11.273-32.5 3.773-15-7.523-63.305-23.344-120.609-74.438-44.586-39.75-74.688-88.844-83.438-103.859-8.75-15-.938-23.125 6.586-30.602 6.734-6.719 15-17.508 22.5-26.266 7.484-8.758 9.984-15.008 14.984-25.008 5-10.016 2.5-18.773-1.25-26.273s-32.898-81.67-46.234-111.326z"/><path fill="#FFF" d="M1036.898 176.091C923.562 62.677 772.859.185 612.297.114 281.43.114 12.172 269.286 12.039 600.137 12 705.896 39.633 809.13 92.156 900.13L7 1211.067l318.203-83.438c87.672 47.812 186.383 73.008 286.836 73.047h.255.003c330.812 0 600.109-269.219 600.25-600.055.055-160.343-62.328-311.108-175.649-424.53zm-424.601 923.242h-.195c-89.539-.047-177.344-24.086-253.93-69.531l-18.227-10.805-188.828 49.508 50.414-184.039-11.875-18.867c-49.945-79.414-76.312-171.188-76.273-265.422.109-274.992 223.906-498.711 499.102-498.711 133.266.055 258.516 52 352.719 146.266 94.195 94.266 146.031 219.578 145.992 352.852-.118 274.999-223.923 498.749-498.899 498.749z"/></svg>
|
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 3.3 KiB |
165
www/index.html
@ -1,165 +0,0 @@
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title id="meta-title">$META_TITLE</title>
|
||||
<meta id="meta-description" name="description" content="$META_DESCRIPTION">
|
||||
<meta id="meta-author" name="author" content="$META_AUTHOR">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,600,700,800&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="css/normalize.css">
|
||||
<link id="theme" rel="stylesheet" href="css/skeleton-light.css">
|
||||
<link rel="stylesheet" href="css/brands.css">
|
||||
<link id="favicon" rel="icon" type="image/png" href="$FAVICON_URL">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="column" style="margin-top: 10%">
|
||||
|
||||
<img id="avatar" class="avatar" src="$AVATAR_URL" srcset="$AVATAR_2X_URL 2x" alt="$AVATAR_ALT">
|
||||
|
||||
<h1 id="name">$NAME</h1>
|
||||
|
||||
<p id="bio">$BIO</p>
|
||||
|
||||
<a id="github" class="button button-github" href="$GITHUB" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/github.svg" alt="GitHub Logo">GitHub</a>
|
||||
<br>
|
||||
|
||||
<a id="twitter" class="button button-twitter" href="$TWITTER" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/twitter.svg" alt="Twitter Logo">Twitter</a>
|
||||
<br>
|
||||
|
||||
<a id="mastodon" class="button button-mastodon" href="$MASTODON" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/mastodon.svg" alt="Mastodon Logo">Mastodon</a>
|
||||
<br>
|
||||
|
||||
<a id="microblog" class="button button-microblog" href="$MICRO_BLOG" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/microblog.svg" alt="Micro.blog Logo">Micro.blog</a>
|
||||
<br>
|
||||
|
||||
<a id="instagram" class="button button-instagram" href="$INSTAGRAM" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/instagram.svg" alt="Instagram Logo">Instagram</a>
|
||||
<br>
|
||||
|
||||
<a id="letterboxd" class="button button-letterboxd" href="$LETTERBOXD" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/letterboxd.svg" alt="Letterboxd Logo">Letterboxd</a>
|
||||
<br>
|
||||
|
||||
<a id="facebook" class="button button-facebook" href="$FACEBOOK" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/facebook.svg" alt="Facebook Logo">Find us on Facebook</a>
|
||||
<br>
|
||||
|
||||
<a id="facebook-messenger" class="button button-messenger" href="$FACEBOOK_MESSENGER" target="_blank"
|
||||
rel="noopener"><img class="icon" src="icons/messenger.svg" alt="Facebook Messenger Logo">Chat on Messenger</a>
|
||||
<br>
|
||||
|
||||
<a id="linkedin" class="button button-linkedin" href="$LINKED_IN" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/linkedin.svg" alt="LinkedIn Logo">LinkedIn</a>
|
||||
<br>
|
||||
|
||||
<a id="youtube" class="button button-youtube" href="$YOUTUBE" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/youtube.svg" alt="YouTube Logo">YouTube</a>
|
||||
<br>
|
||||
|
||||
<a id="discord" class="button button-discord" href="$DISCORD" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/discord.svg" alt="Discord Logo">Discord</a>
|
||||
<br>
|
||||
|
||||
<a id="twitch" class="button button-twitch" href="$TWITCH" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/twitch.svg" alt="Twitch Logo">Twitch</a>
|
||||
<br>
|
||||
|
||||
<a id="producthunt" class="button button-producthunt" href="$PRODUCT_HUNT" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/producthunt.svg" alt="Product Hunt Logo">Product Hunt</a>
|
||||
<br>
|
||||
|
||||
<a id="snapchat" class="button button-snapchat" href="$SNAPCHAT" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/snapchat.svg" alt="Snapchat Logo">Snapchat</a>
|
||||
<br>
|
||||
|
||||
<a id="spotify" class="button button-spotify" href="$SPOTIFY" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/spotify.svg" alt="Spotify Logo">Spotify</a>
|
||||
<br>
|
||||
|
||||
<a id="reddit" class="button button-reddit" href="$REDDIT" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/reddit.svg" alt="Reddit Logo">Reddit</a>
|
||||
<br>
|
||||
|
||||
<a id="medium" class="button button-medium" href="$MEDIUM" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/medium.svg" alt="Medium Logo">Medium</a>
|
||||
<br>
|
||||
|
||||
<a id="pinterest" class="button button-pinterest" href="$PINTEREST" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/pinterest.svg" alt="Pinterest Logo">Follow on Pinterest</a>
|
||||
<br>
|
||||
|
||||
<a id="tiktok" class="button button-tiktok" href="$TIKTOK" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/tiktok.svg" alt="TikTok Logo">TikTok</a>
|
||||
<br>
|
||||
|
||||
<a id="email" class="button button-default" href="mailto:$EMAIL" target="_blank" rel="noopener">$EMAIL_TEXT</a>
|
||||
<br>
|
||||
|
||||
<a id="email-alt" class="button button-default" href="mailto:$EMAIL_ALT" target="_blank"
|
||||
rel="noopener">$EMAIL_ALT_TEXT</a>
|
||||
<br>
|
||||
|
||||
<a id="soundcloud" class="button button-soundcloud" href="$SOUND_CLOUD" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/soundcloud.svg" alt="SoundCloud Logo">SoundCloud</a>
|
||||
<br>
|
||||
|
||||
<a id="figma" class="button button-figma" href="$FIGMA" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/figma.svg" alt="Figma Logo">Figma</a>
|
||||
<br>
|
||||
|
||||
<a id="kit" class="button button-kit" href="$KIT" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/kit.svg" alt="Kit Logo">Kit</a>
|
||||
<br>
|
||||
|
||||
<a id="telegram" class="button button-telegram" href="$TELEGRAM" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/telegram.svg" alt="Telegram Logo">Telegram</a>
|
||||
<br>
|
||||
|
||||
<a id="tumblr" class="button button-tumblr" href="$TUMBLR" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/tumblr.svg" alt="Tumblr Logo">Tumblr</a>
|
||||
<br>
|
||||
|
||||
<a id="steam" class="button button-steam" href="$STEAM" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/steam.svg" alt="Steam Logo">Steam</a>
|
||||
<br>
|
||||
|
||||
<a id="vimeo" class="button button-vimeo" href="$VIMEO" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/vimeo.svg" alt="Vimeo Logo">Vimeo</a>
|
||||
<br>
|
||||
|
||||
<a id="wordpress" class="button button-wordpress" href="$WORDPRESS" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/wordpress.svg" alt="Wordpress Logo">Wordpress</a>
|
||||
<br>
|
||||
|
||||
<a id="goodreads" class="button button-goodreads" href="$GOODREADS" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/goodreads.svg" alt="Goodreads Logo">Goodreads</a>
|
||||
<br>
|
||||
|
||||
<a id="skoob" class="button button-skoob" href="$SKOOB" target="_blank" rel="noopener"><img class="icon"
|
||||
src="icons/skoob.svg" alt="Skoob Logo">Skoob</a>
|
||||
<br>
|
||||
|
||||
<a id="whatsapp" class="button button-whatsapp" href="$WHATSAPP" target="_blank" rel="noopener"><img
|
||||
class="icon" src="icons/whatsapp.svg" alt="WhatsApp Logo">WhatsApp</a>
|
||||
<br>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<p id="footer">$FOOTER</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|