diff --git a/static/NotoColorEmoji.ttf b/static/NotoColorEmoji.ttf new file mode 100644 index 0000000..2c1f104 Binary files /dev/null and b/static/NotoColorEmoji.ttf differ diff --git a/static/copy.svg b/static/copy.svg new file mode 100644 index 0000000..448abf7 --- /dev/null +++ b/static/copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000..6928b70 Binary files /dev/null and b/static/favicon.ico differ diff --git a/static/main.js b/static/main.js new file mode 100644 index 0000000..e61ede5 --- /dev/null +++ b/static/main.js @@ -0,0 +1,215 @@ +var tweetCount = 1, + page = 0, + loading = false, + bigArray = []; +const iosHeight = () => { + document.documentElement.style.setProperty("--ios-height", window.innerHeight + "px"); +}; +window.addEventListener("resize", iosHeight); + +window.onload = () => { + //ios height + iosHeight(); + const contwarn = document.querySelector("#contwarn"); + if (document.cookie.includes("always=true")) { + contwarn.checked = true; + forNow(); + } + + contwarn.addEventListener("change", () => { + if (contwarn.checked) { + document.cookie = `always = true; max-age=15780000; SameSite=None; Secure`; + } else { + document.cookie = "always=false"; + } + }) +} +function cookieTime() { + document.querySelector("#contwarn").checked = true; + document.cookie = `always = true; max-age=15780000; SameSite=None; Secure`; + forNow(); +} +function forNow() { + document.querySelector("#block").style.display = "none"; + fetchNApply(page) + page++; + const tweetCont = document.querySelector(".tweetCont"); + tweetCont.onscroll = async () => { + if (tweetCont.scrollTop > tweetCont.scrollHeight - tweetCont.offsetHeight - 100 && loading == false) { + await fetchNApply(page); + //console.log(page); + page++; + } + }; +} +function fetchNApply(page) { + try { + loading = true; + fetch(`https://fxtwitter.com/api/latest/?tweets=10&page=${page}`) + .then(response => response.json()) + .then(data => { + data.forEach(e => createTweet(e)); + }) + .then(setTimeout(() => loading = false, 500)); + } catch (error) { + console.log(error); + alert(error) + } + +} + +function imgPrev(img) { + const prevImgCont = document.querySelector('.previmgcont'); + img.addEventListener('click', () => { + const clone = img.cloneNode(true); + clone.removeAttribute("width"); + clone.removeAttribute("height"); + clone.className = "previmg"; + prevImgCont.innerHTML = ""; + prevImgCont.appendChild(clone); + prevImgCont.style.display = ''; + }); +} +/* + cont ={ + inner: text + src: link + lazy: bool + href: link + video: link + } + */ +function createEl(tag, cls, cont) { + const el = document.createElement(tag); + if (cls) + el.className = cls; + if (cont) { + if (cont.inner) + el.innerHTML = cont.inner; + if (cont.src) + el.src = cont.src; + if (cont.lazy) + el.setAttribute("loading", "lazy"); + if (cont.href) + el.setAttribute("href", cont.href), + el.setAttribute("rel", "noreferrer"), + el.setAttribute("target", "_blank"); + if (cont.video) { + el.setAttribute("preload", "auto"); + el.setAttribute("controls", ""); + el.setAttribute("disablePictureInPicture", ""); + el.setAttribute("webkit-playsinline", ""); + el.setAttribute("playsinline", ""); + const source = document.createElement("source"); + source.src = cont.video; + source.setAttribute("type", "video/mp4"); + el.appendChild(source); + } + } + return el; +} + +function createTweet(json) { + if (!bigArray.includes(json["tweet"])) { + const tweet = createEl("div", "tweet"); + tweet.id = "t" + tweetCount; + + const auth = createEl("div", "auth"); + + const aimage = createEl("img", "aimage", { + src: json["pfp"], + lazy: true + }); + + const aname = createEl("a", "aname", { + href: `https://twitter.com/${json['screen_name']}` + }); + + aname.appendChild(createEl("div", undefined, { + inner: json['uploader'] + })); + + aname.appendChild(createEl("div", undefined, { + inner: "@" + json['screen_name'] + })); + + const type = createEl("a", "type", { inner: json["type"], href: json["tweet"] }); + + auth.appendChild(aimage); + auth.appendChild(aname); + auth.appendChild(type); + + tweet.appendChild(auth); + + json["description"] = json["description"].replaceAll(/http.*t.co\S+/g, ""); + + + if (json["description"] != "") { + const desc = createEl("div", "desc", { inner: json["description"] }); + tweet.appendChild(desc); + } + + switch (json["type"]) { + case "Text": + //so empty + break; + case "Image": + const media = createEl("img", "media", { + src: json["thumbnail"], + lazy: true + }); + tweet.appendChild(media); + break; + case "Video": + const video = createEl("video", "media", { + video: json["url"] + }); + tweet.appendChild(video); + break; + default: + const video2 = createEl("video", "media", { + video: json["url"] + }); + tweet.appendChild(video2); + //console.log("this should not happen!"); + break; + } + const qrtob = json["qrt"]; + + if ((Object.keys(qrtob).length === 0 && Object.getPrototypeOf(qrtob) === Object.prototype) == false) { + //console.log(json["qrt"]); + const qrt = createEl("div", "quote"); + const qname = createEl("div", "qname", { inner: `QRT of : ${qrtob.handle} (@${qrtob.screenname})` }); + json["qrt"]["desc"] = json["qrt"]["desc"].replaceAll(/http.*t.co\S+/g, ""); + const qdesc = createEl("div", "qdesc", { inner: qrtob.desc }); + qrt.appendChild(qname); + qrt.appendChild(qdesc); + tweet.appendChild(qrt); + } + + + const meta = createEl("div", "meta"); + + const rts = createEl("div", "cont", { inner: `${json["rts"]} Retweets` }); + const lks = createEl("div", "cont", { inner: `${json["likes"]} Likes` }); + + const share = createEl("img", "share", { src: "https://fxtwitter.com/copy.svg" }); + + meta.appendChild(rts); + meta.appendChild(lks); + meta.appendChild(share); + tweet.appendChild(meta); + bigArray.push(json["tweet"]); + document.querySelector(".tweetCont").appendChild(tweet); + document.querySelectorAll('img:not(.aimage,.share)').forEach(img => { + imgPrev(img); + }); + share.addEventListener("click", () => + navigator.clipboard.writeText(json["tweet"].replace("https://t", "https://fxt")) + ); + tweetCount++; + } else { + if (bigArray.length > 100) //pro memory management 😎 + bigArray = []; + } +} \ No newline at end of file diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..4670103 --- /dev/null +++ b/static/style.css @@ -0,0 +1,310 @@ +@font-face { + font-family: NotoColorEmojiLimited; + unicode-range: U+1F1E6-1F1FF; + src: url("https://fxtwitter.com/font.ttf"); + } + +:root { + --ios-height: 100vh; + --top-height: calc(6vh + 2vw); + } + +body{ + margin: 0; + width: 100vw; + height: 100vh; + height: var(--ios-height); + overflow: hidden; +} + + +a{ + color: unset; + text-decoration: unset; +} + +a, +a:link, +a:focus, +a:visited, +a:active, +a:-webkit-any-link, +:link{ + color: unset; + text-decoration: unset; +} + +a:hover{ + color: #8ebf42; +} + +::-webkit-scrollbar { + width: 1vmin; + +} + +::-webkit-scrollbar-track { + background: #171717; + border-radius: .5vmin; + margin: 2vmin 0 2vmin; +} + +::-webkit-scrollbar-thumb { + background: #e8e8e8; + border-radius: .5vmin; +} + +::-webkit-scrollbar-thumb:hover { + background: #8ebf42; +} + +::selection { + color: #171717; + background-color: #e8e8e8; +} + +.base{ + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + font-family: 'Paytone One','NotoColorEmojiLimited', sans-serif; + background-color: #222222; + color: #e8e8e8; +} + +.top{ + height: var(--top-height); + display: flex; + flex-direction: column; + justify-content: center; + text-align: center; + box-sizing: border-box; + font-size: 9vmin; + padding: 1vmin 0 1vmin; + /*margin: 0 20vw 0; + border-bottom: #e8e8e8 .1vmin solid;*/ + box-shadow: rgba(0, 0, 0, 0.2) 0px 1px 2px 0px; +} +.top:hover{ + color: #8ebf42; + cursor: pointer; +} +.bottom { + flex: 1; + display: flex; + flex-direction: row; +} + +.tweetCont{ + flex: 2; + padding: calc( 1vh + .25vw ) 10% 0; + + overflow-y: auto; + max-height: calc( var(--ios-height) - var(--top-height) - calc( 1vh + .25vw ) ); +} + +.tweet{ + display: flex; + position: relative; + flex-direction: column; + background-color: #171717; + margin: 0 0 calc( 2vh + .5vw ); + padding: 1vmin 0 1vmin; + box-shadow: rgba(0, 0, 0, 0.4) 0px 4px 7px; + font-size: 18px; +} + +.side{ + flex: 1; + overflow-y: auto; + max-height: calc( var(--ios-height) - var(--top-height) - calc( 1vh + .25vw ) ); +} +.by{ + color: #8ebf42; +} +.by:hover{ + color: #F74843; +} +/* Tweet */ +.desc{ + padding: 0 calc( 1.5vh + .25vw ) 0; +} + +.auth,.meta{ + display: flex; + flex-direction: row; + gap: 1%; + font-size: 1.2em; + padding: 0 calc( .5vh + .125vw ) 0; +} +.aname{ + display: flex; + flex-direction: column; + justify-content: space-evenly; +} +.aname,.type{ + flex: 1; +} +.type{ + text-align: right; + margin: auto 2% auto; +} +.aimage{ + border-radius: 50%; + border: #8ebf42 .3vmin solid; + height: 100%; + width: auto; + margin: auto; + /* No Drag */ + -webkit-user-drag: none; + -khtml-user-drag: none; + -moz-user-drag: none; + -o-user-drag: none; +} + +.media{ + box-sizing: border-box; + width: 100%; + height: auto; + max-height: 60vh; + object-fit: scale-down; + background-color: black; +} +.quote{ + margin: auto; + width: 90%; + border-radius: .5vmin; + border: #222222 solid .5vmin; + padding: .5vmin; +} +.qname{ + color: #8ebf42; +} +.cont,.date{ + margin: auto 0 auto; + +} +.date{ + margin-left: 2%; +} +.share{ + margin-left: auto; + height: 2em; + transition-duration: .2s; + transition-timing-function: ease-in-out; +} +.share:hover{ + transform: scale(.8); +} +.share:active{ + transform: scale(2); +} +.auth,.desc,.media,.quote{ + margin-bottom: 2vmin; +} + +/* Settings */ +.settings,.info{ + font-size: 35px; + padding: 1vmin; +} +.opt{ + font-size: .49em; + user-select: none; + white-space: nowrap; +} +.Iinner{ + font-size: .5em; +} +/* Preview */ + +img:not(.previmg,.aimage){ + cursor: pointer; + transition-duration: .2s; + transition-timing-function: ease-in-out; +} + +.previmgcont { + position: fixed; + display: flex; + justify-content: center; + flex-direction: column; + top: 0%; + left: 0%; + width: 100%; + height: 100%; + z-index: 101; + background-color: #171717b7; +} + +.previmg { + margin: auto; + cursor: zoom-out !important; + max-width: 100vw; + max-height: 100vh; + min-width: 100vw; + object-fit: contain; +} + +/* Block */ + +#block { + position: fixed; + box-sizing: border-box; + width: 100%; + height: 100%; + z-index: 99; + background: #222222; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(1.75vh + .25vw); + text-align: center; +} +#block .warn{ + font-size: 2em; + color: #F74843; + text-align: center; +} +.boptions{ + display: flex; + flex-direction: row; + gap: 1.5vmin; +} +.boptions>div { + padding: 2vh 2vw; + border: solid .5vmin #c5c5c5; + color: #e8e8e8; + border-radius: .5vmin; + transition-duration: .15s; + transition-timing-function: ease-in-out; + cursor: pointer; + user-select: none; + font-size: 1em; +} + +.boptions>div:hover { + transform: scale(1.02); + border: solid .5vmin #8ebf42; +} + +.boptions>div:active { + transform: scale(.98); +} + +@media (max-aspect-ratio: 4/4), (max-height: 500px), (max-width: 1100px){ + .side{ + display: none; + } + .tweet{ + font-size: 12px; + } + .tweetCont{ + padding: 0 5% 0; + } + .boptions{ + flex-direction: column; + } +}