Commit 6eb6c36e authored by dasunx's avatar dasunx

automated answer ui updated, custom hooks for debounce, polling and timeout has been added

parent 79c3cb92
/* PrismJS 1.25.0
https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript+abap+abnf+actionscript+ada+agda+al+antlr4+apacheconf+apex+apl+applescript+aql+arduino+arff+asciidoc+aspnet+asm6502+asmatmel+autohotkey+autoit+avisynth+avro-idl+bash+basic+batch+bbcode+bicep+birb+bison+bnf+brainfuck+brightscript+bro+bsl+c+csharp+cpp+cfscript+chaiscript+cil+clojure+cmake+cobol+coffeescript+concurnas+csp+coq+crystal+css-extras+csv+cypher+d+dart+dataweave+dax+dhall+diff+django+dns-zone-file+docker+dot+ebnf+editorconfig+eiffel+ejs+elixir+elm+etlua+erb+erlang+excel-formula+fsharp+factor+false+firestore-security-rules+flow+fortran+ftl+gml+gap+gcode+gdscript+gedcom+gherkin+git+glsl+gn+go+graphql+groovy+haml+handlebars+haskell+haxe+hcl+hlsl+hoon+http+hpkp+hsts+ichigojam+icon+icu-message-format+idris+ignore+inform7+ini+io+j+java+javadoc+javadoclike+javastacktrace+jexl+jolie+jq+jsdoc+js-extras+json+json5+jsonp+jsstacktrace+js-templates+julia+keepalived+keyman+kotlin+kumir+kusto+latex+latte+less+lilypond+liquid+lisp+livescript+llvm+log+lolcode+lua+magma+makefile+markdown+markup-templating+matlab+maxscript+mel+mermaid+mizar+mongodb+monkey+moonscript+n1ql+n4js+nand2tetris-hdl+naniscript+nasm+neon+nevod+nginx+nim+nix+nsis+objectivec+ocaml+opencl+openqasm+oz+parigp+parser+pascal+pascaligo+psl+pcaxis+peoplecode+perl+php+phpdoc+php-extras+plsql+powerquery+powershell+processing+prolog+promql+properties+protobuf+pug+puppet+pure+purebasic+purescript+python+qsharp+q+qml+qore+r+racket+cshtml+jsx+tsx+reason+regex+rego+renpy+rest+rip+roboconf+robotframework+ruby+rust+sas+sass+scss+scala+scheme+shell-session+smali+smalltalk+smarty+sml+solidity+solution-file+soy+sparql+splunk-spl+sqf+sql+squirrel+stan+iecst+stylus+swift+systemd+t4-templating+t4-cs+t4-vb+tap+tcl+tt2+textile+toml+tremor+turtle+twig+typescript+typoscript+unrealscript+uri+v+vala+vbnet+velocity+verilog+vhdl+vim+visual-basic+warpscript+wasm+web-idl+wiki+wolfram+wren+xeora+xml-doc+xojo+xquery+yaml+yang+zig&plugins=line-highlight+line-numbers+custom-class+highlight-keywords+normalize-whitespace */
code[class*=language-],pre[class*=language-]{color:#ccc;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}
pre[data-line]{position:relative;padding:1em 0 1em 3em}.line-highlight{position:absolute;left:0;right:0;padding:inherit 0;margin-top:1em;background:hsla(24,20%,50%,.08);background:linear-gradient(to right,hsla(24,20%,50%,.1) 70%,hsla(24,20%,50%,0));pointer-events:none;line-height:inherit;white-space:pre}@media print{.line-highlight{-webkit-print-color-adjust:exact;color-adjust:exact}}.line-highlight:before,.line-highlight[data-end]:after{content:attr(data-start);position:absolute;top:.4em;left:.6em;min-width:1em;padding:0 .5em;background-color:hsla(24,20%,50%,.4);color:#f4f1ef;font:bold 65%/1.5 sans-serif;text-align:center;vertical-align:.3em;border-radius:999px;text-shadow:none;box-shadow:0 1px #fff}.line-highlight[data-end]:after{content:attr(data-end);top:auto;bottom:.4em}.line-numbers .line-highlight:after,.line-numbers .line-highlight:before{content:none}pre[id].linkable-line-numbers span.line-numbers-rows{pointer-events:all}pre[id].linkable-line-numbers span.line-numbers-rows>span:before{cursor:pointer}pre[id].linkable-line-numbers span.line-numbers-rows>span:hover:before{background-color:rgba(128,128,128,.2)}
pre[class*=language-].line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}pre[class*=language-].line-numbers>code{position:relative;white-space:inherit}.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows>span{display:block;counter-increment:linenumber}.line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right}
...@@ -3,11 +3,14 @@ import { publicFetch } from '../../util/fetcher' ...@@ -3,11 +3,14 @@ import { publicFetch } from '../../util/fetcher'
import AutomatedAnswer from '../automated-answer' import AutomatedAnswer from '../automated-answer'
import NoAutomatedAnswer from '../automated-answer/no-automated-answer' import NoAutomatedAnswer from '../automated-answer/no-automated-answer'
import { Spinner } from '../icons' import { Spinner } from '../icons'
import { useInterval } from '../../hooks/useInterval'
import styles from './automated-answer-container.module.css' import styles from './automated-answer-container.module.css'
const AutomatedAnswerContainer = ({ question_id }) => { const AutomatedAnswerContainer = ({ question_id }) => {
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const [polled, setPolled] = useState(500)
const [tries, setTries] = useState(5)
const [answer, setAnswer] = useState() const [answer, setAnswer] = useState()
const [isAnswerGenerating, setIsAnswertGenerating] = useState(false) const [isAnswerGenerating, setIsAnswertGenerating] = useState(false)
const [error, setError] = useState({ const [error, setError] = useState({
...@@ -17,27 +20,37 @@ const AutomatedAnswerContainer = ({ question_id }) => { ...@@ -17,27 +20,37 @@ const AutomatedAnswerContainer = ({ question_id }) => {
message: '' message: ''
}) })
useEffect(() => { const fetchAutomatedAnswer = async () => {
const fetchAutomatedAnswer = async () => { if (tries > 0) {
try { setTries(tries - 1)
const response = await publicFetch.get(`automatedanswer/${question_id}`) setPolled(null)
if (response.status == 200) {
console.log('RESPONSE IS HERE AND IT IS')
const { isLoading, isComplete } = response.data.automatedanswer
if (isLoading && !isComplete) {
setIsAnswertGenerating(true)
setServerError(102)
} else {
setAnswer(response.data)
}
} else setServerError({ response })
} catch (error) {
setServerError(error)
}
setLoading(false)
} }
fetchAutomatedAnswer() try {
}, [question_id]) const response = await publicFetch.get(`automatedanswer/${question_id}`)
if (response.status == 200) {
console.log('RESPONSE IS HERE AND IT IS')
const { isLoading, isComplete } = response.data.automatedanswer
if (isLoading && !isComplete) {
setIsAnswertGenerating(true)
setServerError(102)
setPolled(1000)
} else {
setIsAnswertGenerating(false)
setAnswer(response.data)
setPolled(null)
}
} else setServerError({ response })
} catch (error) {
setServerError(error)
}
setLoading(false)
}
useInterval(async () => {
console.log('Checking')
await fetchAutomatedAnswer()
}, polled)
const setServerError = (error) => { const setServerError = (error) => {
if (error == 102) { if (error == 102) {
...@@ -73,6 +86,7 @@ const AutomatedAnswerContainer = ({ question_id }) => { ...@@ -73,6 +86,7 @@ const AutomatedAnswerContainer = ({ question_id }) => {
question_id={question_id} question_id={question_id}
error={error} error={error}
isGenerating={true} isGenerating={true}
bindIsGenerating={setIsAnswertGenerating}
/> />
) )
} else if (answer != null) { } else if (answer != null) {
...@@ -83,6 +97,7 @@ const AutomatedAnswerContainer = ({ question_id }) => { ...@@ -83,6 +97,7 @@ const AutomatedAnswerContainer = ({ question_id }) => {
question_id={question_id} question_id={question_id}
error={error} error={error}
isGenerating={false} isGenerating={false}
bindIsGenerating={setIsAnswertGenerating}
/> />
) )
} }
...@@ -95,7 +110,7 @@ const AutomatedAnswerContainer = ({ question_id }) => { ...@@ -95,7 +110,7 @@ const AutomatedAnswerContainer = ({ question_id }) => {
<h2>Automated Answer</h2> <h2>Automated Answer</h2>
</div> </div>
</div> </div>
<div className={styles.wrapper}>{getBody()}</div> <div className={styles.wrapper}>{getBody(answer)}</div>
</div> </div>
) )
} }
......
import React, { useContext } from 'react' import React, { useContext, useState } from 'react'
import { FetchContext } from '../../../store/fetch' import { FetchContext } from '../../../store/fetch'
import styles from './no-automated-answer.module.css' import styles from './no-automated-answer.module.css'
import { Spinner } from '../../icons' import { Spinner } from '../../icons'
const NoAutomatedAnswer = ({ question_id, error, isGenerating }) => { const NoAutomatedAnswer = ({
question_id,
error,
isGenerating,
bindIsGenerating,
setPollingTime
}) => {
const [isAnswerGenerating, setIsAnswerGenerating] = useState(isGenerating)
const { authAxios } = useContext(FetchContext) const { authAxios } = useContext(FetchContext)
const req_body = {
question_id
}
const requestAutomatedAnswer = async () => {
try {
const response = await authAxios.post('automatedanswer', req_body)
console.log(response)
if (response.status == 201) {
setIsAnswerGenerating(true)
bindIsGenerating(true)
setPollingTime(1000)
}
} catch (err) {
console.log(err)
}
}
const { status, action, button, message } = error const { status, action, button, message } = error
return ( return (
<div className={styles.no_automated_answer}> <div className={styles.no_automated_answer}>
<h1 className={styles.title}>{message}</h1> <h1 className={styles.title}>{message}</h1>
{isGenerating ? ( {isAnswerGenerating ? (
<Spinner className={styles.spinner} /> <Spinner className={styles.spinner} />
) : ( ) : (
<button className={styles.generate_btn}>{button}</button> <button
className={styles.generate_btn}
onClick={requestAutomatedAnswer}
>
{button}
</button>
)} )}
</div> </div>
) )
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
box-shadow: inset 0 1px 0 0 rgb(255 255 255 / 40%); box-shadow: inset 0 1px 0 0 rgb(255 255 255 / 40%);
padding: 0.8em; padding: 0.8em;
margin: 1em; margin: 1em;
cursor: pointer;
} }
.title { .title {
......
import React from 'react' import React, { useEffect } from 'react'
import styles from './BlogArticle.module.css' import styles from './BlogArticle.module.css'
const BlogArticle = ({ article }) => { import Prism from 'prismjs'
const BlogArticle = ({ article, source }) => {
useEffect(() => {
console.log(source)
if (source === 'Medium') {
var childs = document.getElementById('description').children
var i = 0
for (i = 0; i <= childs.length; i++) {
try {
if (childs[i].tagName == 'PRE') {
hljs.highlightElement(childs[i])
}
} catch (error) {
console.log('ALL PRE TAGS ARE PRETTIFIED')
}
}
Prism.highlightAll()
}
}, [])
const createMarkup = () => { const createMarkup = () => {
return { __html: article.description } return { __html: article.description }
} }
...@@ -9,6 +28,7 @@ const BlogArticle = ({ article }) => { ...@@ -9,6 +28,7 @@ const BlogArticle = ({ article }) => {
<div className={styles.blog_article}> <div className={styles.blog_article}>
<span className={styles.title}>{article.title}</span> <span className={styles.title}>{article.title}</span>
<div <div
id="description"
className={styles.description} className={styles.description}
dangerouslySetInnerHTML={createMarkup()} dangerouslySetInnerHTML={createMarkup()}
></div> ></div>
......
...@@ -87,14 +87,36 @@ ...@@ -87,14 +87,36 @@
pre { pre {
padding: 20px; padding: 20px;
background-color: var(black); background-color: var(black);
font-size: 1.1em;
font-family: 'Roboto Mono', monospace !important; font-family: 'Roboto Mono', monospace !important;
max-width: 780px;
} }
pre::first-line { pre::first-line {
font-weight: bold; font-weight: bold;
} }
pre::-webkit-scrollbar {
width: 5px;
height: 8px;
}
/* Track */
pre::-webkit-scrollbar-track {
background: #f1f1f1;
}
/* Handle */
pre::-webkit-scrollbar-thumb {
width: 5px;
background: rgb(44, 0, 95);
}
/* Handle on hover */
pre::-webkit-scrollbar-thumb:hover {
background: #555;
}
ul { ul {
margin-left: 10px; margin-left: 10px;
padding: 0; padding: 0;
......
import React, { useState } from 'react' import React, { useState } from 'react'
import AutomatedAnswerSwiper from '../automated-answer-swiper/AutomatedAnswerSwiper' import AutomatedAnswerSwiper from '../automated-answer-swiper/AutomatedAnswerSwiper'
import BlogArticle from '../blog-article/BlogArticle' import BlogArticle from '../blog-article/BlogArticle'
import NoResource from '../no-resource/NoResource'
import ResourcePreview from '../resource-preview/ResourcePreview'
import ShowHide from '../show-hide/ShowHide' import ShowHide from '../show-hide/ShowHide'
import styles from './BlogsWrapper.module.css' import styles from './BlogsWrapper.module.css'
...@@ -10,26 +12,26 @@ const BlogsWrapper = ({ source, articles, resources }) => { ...@@ -10,26 +12,26 @@ const BlogsWrapper = ({ source, articles, resources }) => {
<> <>
{articles && articles.length > 0 ? ( {articles && articles.length > 0 ? (
<> <>
<h1 className={styles.h1}> <ResourcePreview
Here {articles.length > 1 ? 'are' : 'is'} {articles.length} article articles={articles}
{articles.length > 1 && 's'} I found on {source} source={source}
<ShowHide show={show} onClick={() => setShow(!show)} /> show={show}
</h1> setShow={setShow}
/>
{show && ( {show && (
<div className={styles.blogs_wrapper}> <div className={styles.blogs_wrapper}>
<AutomatedAnswerSwiper> <AutomatedAnswerSwiper>
{articles.map((article, i) => { {articles.map((article, i) => {
return <BlogArticle key={i} article={article} /> return (
<BlogArticle key={i} article={article} source={source} />
)
})} })}
</AutomatedAnswerSwiper> </AutomatedAnswerSwiper>
</div> </div>
)} )}
</> </>
) : ( ) : (
<h1> <NoResource source={source} />
No {source} articles found for this question. Please refer the extra
resouces to find more info
</h1>
)} )}
</> </>
) )
......
.blogs_wrapper { .blogs_wrapper {
margin-top: 0.5em;
background-color: #2d2d2d; background-color: #2d2d2d;
padding: 0.8em; padding: 0.8em;
border-radius: 2px; border-radius: 2px;
......
import React from 'react'
const CopyIcon = (props) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" {...props}>
<g data-name="Layer 3">
<path d="M40.63,13H12.38A4.69,4.69,0,0,0,7.7,17.67V57.31A4.69,4.69,0,0,0,12.38,62H40.63a4.69,4.69,0,0,0,4.69-4.69V17.67A4.69,4.69,0,0,0,40.63,13Zm2.69,44.33A2.69,2.69,0,0,1,40.63,60H12.38A2.69,2.69,0,0,1,9.7,57.31V17.67A2.69,2.69,0,0,1,12.38,15H40.63a2.69,2.69,0,0,1,2.69,2.69Z" />
<path d="M51.74,2H23.26a4.58,4.58,0,0,0-4.58,4.57v3.55a1,1,0,0,0,2,0V6.57A2.58,2.58,0,0,1,23.26,4H51.74A2.57,2.57,0,0,1,54.3,6.57V46.44A2.58,2.58,0,0,1,51.74,49H48.5a1,1,0,0,0,0,2h3.24a4.58,4.58,0,0,0,4.57-4.58V6.57A4.57,4.57,0,0,0,51.74,2Z" />
</g>
</svg>
)
}
export default CopyIcon
import React from 'react'
const TelegramLogo = (props) => {
return (
<svg
fill="none"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 500 500"
{...props}
>
<path
d="M250 500c138.071 0 250-111.929 250-250S388.071 0 250 0 0 111.929 0 250s111.929 250 250 250z"
fill="#34aadf"
/>
<path
d="M104.047 247.832s125-51.3 168.352-69.364c16.619-7.225 72.977-30.347 72.977-30.347s26.012-10.115 23.844 14.451c-.723 10.116-6.503 45.52-12.283 83.815-8.671 54.191-18.064 113.439-18.064 113.439s-1.445 16.619-13.728 19.509-32.515-10.115-36.127-13.006c-2.891-2.167-54.191-34.682-72.977-50.578-5.058-4.335-10.838-13.005.722-23.121 26.012-23.844 57.081-53.468 75.867-72.254 8.671-8.671 17.341-28.902-18.786-4.336-51.3 35.405-101.878 68.642-101.878 68.642s-11.561 7.225-33.237.722c-21.677-6.502-46.966-15.173-46.966-15.173s-17.34-10.838 12.284-22.399z"
fill="#fff"
/>
</svg>
)
}
export default TelegramLogo
...@@ -6,4 +6,6 @@ export { default as Logo } from './Logo' ...@@ -6,4 +6,6 @@ export { default as Logo } from './Logo'
export { default as Menu } from './Menu' export { default as Menu } from './Menu'
export { default as Search } from './Search' export { default as Search } from './Search'
export { default as Spinner } from './Spinner' export { default as Spinner } from './Spinner'
export { default as World } from './World' export { default as World } from './World'
\ No newline at end of file export { default as TelegramLogo } from './TelegramLogo'
export { default as CopyIcon } from './Copy'
...@@ -4,9 +4,11 @@ ...@@ -4,9 +4,11 @@
margin-left: auto; margin-left: auto;
} }
.tagContainer { .tagContainer_wrapper {
position: sticky; position: sticky;
top: 74px; top: 74px;
}
.tagContainer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding-left: 12px; padding-left: 12px;
......
...@@ -6,29 +6,33 @@ import Tag from '../../tag' ...@@ -6,29 +6,33 @@ import Tag from '../../tag'
import { Spinner } from '../../icons' import { Spinner } from '../../icons'
import styles from './extra.module.css' import styles from './extra.module.css'
import TelegramBot from '../../telegram-bot-container/TelegramBot'
const Extra = ({ marginTop = 24 }) => { const Extra = ({ marginTop = 24 }) => {
const { tagState } = useContext(TagContext) const { tagState } = useContext(TagContext)
return ( return (
<div className={styles.container}> <div className={styles.container}>
<div <div className={styles.tagContainer_wrapper}>
className={styles.tagContainer} <div
style={{ marginTop: `${marginTop}px` }} className={styles.tagContainer}
> style={{ marginTop: `${marginTop}px` }}
<h2>Popular Tags</h2> >
{!tagState && ( <h2>Popular Tags</h2>
<div className="loading"> {!tagState && (
<Spinner /> <div className="loading">
<Spinner />
</div>
)}
<div className={styles.popularTags}>
{tagState?.map((tag) => (
<Tag key={tag._id} count={tag.count}>
{tag._id}
</Tag>
))}
</div> </div>
)}
<div className={styles.popularTags}>
{tagState?.map((tag) => (
<Tag key={tag._id} count={tag.count}>
{tag._id}
</Tag>
))}
</div> </div>
<TelegramBot />
</div> </div>
</div> </div>
) )
......
import React from 'react' import React from 'react'
import cn from 'classnames' import cn from 'classnames'
import CONST from '../../constants' import CONST from '../../constants'
import useWindowSize from '../../hooks/useWindowSize' import useWindowSize from '../../hooks/useWindowSize'
...@@ -13,6 +12,7 @@ import styles from './layout.module.css' ...@@ -13,6 +12,7 @@ import styles from './layout.module.css'
const Layout = ({ extra = true, children }) => { const Layout = ({ extra = true, children }) => {
const size = useWindowSize() const size = useWindowSize()
return ( return (
<div className={styles.layout}> <div className={styles.layout}>
<Header /> <Header />
......
import React from 'react'
import Styles from './NoResource.module.css'
const NoResource = ({ source, message }) => {
return (
<div className={Styles.noresource}>
<h1>
No resources found from <b>{source}</b> for this question. Please refer
the extra resouces section to find more info.
</h1>
</div>
)
}
export default NoResource
.noresource {
padding: 20px 5px;
background-color: #1e225f;
color: #418df7;
margin: 10px 0;
border-radius: 5px;
h1 {
font-size: 18px;
}
}
import React, { useState, useEffect } from 'react'
import Styles from './ResourcePreview.module.css'
import ShowHide from '../show-hide/ShowHide'
const ResourcePreview = ({ articles, source, show, setShow }) => {
const [thumbnail, setThumbnail] = useState('')
const getThumbnail = () => {
articles.forEach((element) => {
if (
!(
element.thumbnail == 'undefined' ||
element.thumbnail == undefined ||
element.thumbnail == null ||
element.thumbnail == '' ||
!element.thumbnail
)
) {
setThumbnail(element.thumbnail)
return
}
})
}
useEffect(() => {
getThumbnail()
}, [])
const onError = (e) => {
console.log('ON ERROR')
e.target.src =
'https://www.mtu.edu/cs/images/trees-dark-blue-icons-6x6-card1200.jpg'
}
return (
<div
className={
show ? Styles.article_preview_hide : Styles.article_preview_active
}
>
<div className={show ? Styles.preview : Styles.preview}>
<img
src={thumbnail}
onError={onError}
alt="article image"
className={Styles.preview_img}
/>
</div>
<div className={Styles.action_bar}>
<h1 className={Styles.title}>
Here {articles.length > 1 ? 'are' : 'is'} {articles.length} article
{articles.length > 1 && 's'} I found on {source}
</h1>
<ShowHide show={show} onClick={() => setShow(!show)} />
</div>
</div>
)
}
export default ResourcePreview
.article_preview_active {
border-radius: 10px;
height: 220px;
background-color: blue;
margin: 10px 0;
position: relative;
display: flex;
flex-direction: column-reverse;
overflow: hidden;
transition: all 0.5s cubic-bezier(0.47, 0, 0.75, 0.72);
}
.article_preview_hide {
border-radius: 10px 10px 0 0;
height: 120px;
background-color: blue;
position: relative;
display: flex;
flex-direction: column-reverse;
overflow: hidden;
transition: all 0.5s cubic-bezier(0.47, 0, 0.75, 0.72);
}
.title {
font-size: 2em !important;
display: flex;
justify-content: space-between;
align-items: center;
}
.action_bar {
display: flex;
flex-direction: column;
align-items: center;
z-index: 2;
background: linear-gradient(0deg, #752d87, transparent);
padding: 80px 0 10px 0;
}
.preview {
position: absolute;
top: 0;
}
.preview_img {
max-width: 100%;
}
...@@ -8,11 +8,11 @@ const ShowHide = ({ onClick, show }) => { ...@@ -8,11 +8,11 @@ const ShowHide = ({ onClick, show }) => {
<div onClick={onClick} className={styles.show_hide}> <div onClick={onClick} className={styles.show_hide}>
{show ? ( {show ? (
<span> <span>
<EyeCloseIcon width={24} height={24} color="red" /> Hide <span>Hide</span> <EyeCloseIcon width={24} height={24} color="red" />
</span> </span>
) : ( ) : (
<span> <span>
<EyeIcon width={24} height={24} color="white" /> Show <span>Show</span> <EyeIcon width={24} height={24} color="white" />
</span> </span>
)} )}
</div> </div>
......
.show_hide { .show_hide {
cursor: pointer; cursor: pointer;
font-size: 1.5rem; font-size: 1.5rem;
background-color: var(--black-800); background-color: #2427296b;
padding: 2px 10px; padding: 4px 24px;
border-radius: 5px; border-radius: 5px;
margin: auto;
border: 2px solid black;
width: 160px;
align-items: center;
span { span {
display: flex; display: flex;
align-items: center; align-items: center;
flex-direction: row;
justify-content: space-evenly;
* { * {
margin-right: 5px; margin-right: 5px;
} }
......
import React, { useEffect, useState } from 'react' import React, { useState } from 'react'
import SimilarQuestion from './similar-question' import SimilarQuestion from './similar-question'
import { Title } from '../styled-text-spans/index' import { Title } from '../styled-text-spans/index'
import styles from './similar-question-suggestions.module.css' import styles from './similar-question-suggestions.module.css'
import useDebounce from '../../hooks/useDebounce'
import { Spinner } from '../icons'
const SimilarQuestionSuggestions = ({ title, description, tags }) => { const SimilarQuestionSuggestions = ({ title, description, tags }) => {
const [isLoading, setIsLoading] = useState(true)
const [similarQuestions, setSimilarQuestions] = useState([ const [similarQuestions, setSimilarQuestions] = useState([
{ {
url: 'https://dasunx.com', url: 'https://dasunx.com',
...@@ -40,19 +44,28 @@ const SimilarQuestionSuggestions = ({ title, description, tags }) => { ...@@ -40,19 +44,28 @@ const SimilarQuestionSuggestions = ({ title, description, tags }) => {
comments: 6 comments: 6
} }
]) ])
useEffect(() => {
// TODO - Fetch similar questions from backend using the title/ description useDebounce(
console.log('TITLE CHANGED') () => {
}, []) setIsLoading(false)
},
1000,
[title]
)
return ( return (
<div className={styles.container}> <div className={styles.container}>
<h1> <h1>
Similar questions -{' '} Similar questions -{' '}
<Title title={title} textColor="var(--main-purple)" /> <Title title={title} textColor="var(--main-purple)" />
</h1> </h1>
{similarQuestions.map((question, index) => { {isLoading ? (
return <SimilarQuestion question={question} key={index} /> <Spinner className={styles.spinner} />
})} ) : (
similarQuestions.map((question, index) => {
return <SimilarQuestion question={question} key={index} />
})
)}
</div> </div>
) )
} }
......
...@@ -9,3 +9,11 @@ ...@@ -9,3 +9,11 @@
flex-direction: column; flex-direction: column;
max-width: 950px; max-width: 950px;
} }
.spinner {
width: 60px;
height: 60px;
justify-content: center;
text-align: center;
margin: auto;
}
import React from 'react' import React, { useEffect } from 'react'
import Prism from 'prismjs'
import styles from './stof-answer.module.css' import styles from './stof-answer.module.css'
const StackOverflowAnswer = ({ stof }) => { const StackOverflowAnswer = ({ stof }) => {
useEffect(() => {
// document.querySelectorAll('pre code').forEach((block) => {
// Prism.highlightElement(block)
// })
// Prism.highlightAll()
hljs.highlightAll()
Prism.highlightAll()
}, [stof])
const createMarkup = () => { const createMarkup = () => {
return { __html: stof.content } return { __html: stof.content }
} }
......
...@@ -7,6 +7,10 @@ ...@@ -7,6 +7,10 @@
font-family: 'Open Sans', sans-serif; font-family: 'Open Sans', sans-serif;
margin-bottom: 3em; margin-bottom: 3em;
color: white; color: white;
img {
max-width: 780px;
}
} }
.h { .h {
......
import React from 'react'
import { TelegramLogo, CopyIcon } from '../icons'
import Styles from './TelegramBot.module.css'
const TelegramBot = () => {
const [isCopiedTag, setIsCopiedTag] = React.useState({
isCopied: false,
isError: false,
message: ''
})
const { isCopied, isError, message } = isCopiedTag
const onClick = () => {
navigator.clipboard.writeText('probexpertbot').then(
() => {
setIsCopiedTag({
isCopied: true,
isError: false,
message: 'Copied!'
})
},
() => {
setIsCopiedTag({
isCopied: false,
isError: true,
message: 'Error!'
})
}
)
}
return (
<div className={Styles.telegram_bot_container}>
<div className={Styles.telegram_bot_title}>
Use ProbExpert Telegram bot
</div>
<a href="https://t.me/probexpertbot" target="_blank">
<TelegramLogo className={Styles.telegram_bot_logo} />
</a>
<div className={Styles.telegram_bot_tag}>
<span>@probexpertbot</span>{' '}
<CopyIcon className={Styles.telegram_bot_tag_icon} onClick={onClick} />
</div>
{isCopied ? (
<div className={Styles.telegram_bot_tag_copied}>{message}</div>
) : (
isError && (
<div className={Styles.telegram_bot_tag_error}>{message}</div>
)
)}
</div>
)
}
export default TelegramBot
.telegram_bot_container {
display: flex;
flex-direction: column;
align-items: center;
background: #f5f5f5;
margin: 20px 0;
border-radius: 10px;
padding: 20px 10px;
gap: 15px;
}
.telegram_bot_title {
font-size: 16px;
color: #0c0cc4;
font-weight: 600;
/* text-transform: capitalize; */
text-transform: uppercase;
text-align: center;
}
.telegram_bot_logo {
width: 80px;
height: 80px;
}
.telegram_bot_tag {
padding: 10px;
display: flex;
flex-direction: row;
justify-content: space-evenly;
background-color: white;
margin: 10px 0;
border-radius: 10px;
width: 70%;
font-family: 'Roboto Mono';
span {
font-size: 14px;
}
}
.telegram_bot_tag_icon {
width: 20px;
cursor: pointer;
}
.telegram_bot_tag_copied {
color: green;
font-weight: 600;
}
.telegram_bot_tag_error {
color: red;
font-weight: 600;
}
import { useEffect } from 'react'
import useTimeout from './useTimeout'
export default function useDebounce(callback, delay, dependencies) {
const { reset, clear } = useTimeout(callback, delay)
useEffect(reset, [...dependencies, reset])
useEffect(clear, [])
}
import { useEffect, useRef } from 'react'
export function useInterval(callback, delay) {
const savedCallback = useRef()
useEffect(() => {
savedCallback.current = callback
}, [callback])
useEffect(() => {
function tick() {
savedCallback.current()
}
if (delay != null) {
const id = setInterval(tick, delay)
return () => {
clearInterval(id)
}
}
}, [callback, delay])
}
import { useCallback, useEffect, useRef } from 'react'
export default function useTimeout(callback, delay) {
const callbackRef = useRef(callback)
const timeoutRef = useRef()
useEffect(() => {
callbackRef.current = callback
}, [callback])
const set = useCallback(() => {
timeoutRef.current = setTimeout(() => callbackRef.current(), delay)
}, [delay])
const clear = useCallback(() => {
timeoutRef.current && clearTimeout(timeoutRef.current)
}, [])
useEffect(() => {
set()
return clear
}, [delay, set, clear])
const reset = useCallback(() => {
clear()
set()
}, [clear, set])
return { reset, clear }
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><g data-name="Layer 3"><path d="M40.63,13H12.38A4.69,4.69,0,0,0,7.7,17.67V57.31A4.69,4.69,0,0,0,12.38,62H40.63a4.69,4.69,0,0,0,4.69-4.69V17.67A4.69,4.69,0,0,0,40.63,13Zm2.69,44.33A2.69,2.69,0,0,1,40.63,60H12.38A2.69,2.69,0,0,1,9.7,57.31V17.67A2.69,2.69,0,0,1,12.38,15H40.63a2.69,2.69,0,0,1,2.69,2.69Z"/><path d="M51.74,2H23.26a4.58,4.58,0,0,0-4.58,4.57v3.55a1,1,0,0,0,2,0V6.57A2.58,2.58,0,0,1,23.26,4H51.74A2.57,2.57,0,0,1,54.3,6.57V46.44A2.58,2.58,0,0,1,51.74,49H48.5a1,1,0,0,0,0,2h3.24a4.58,4.58,0,0,0,4.57-4.58V6.57A4.57,4.57,0,0,0,51.74,2Z"/></g></svg>
\ No newline at end of file
<svg fill="none" height="2500" width="2500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><path d="M250 500c138.071 0 250-111.929 250-250S388.071 0 250 0 0 111.929 0 250s111.929 250 250 250z" fill="#34aadf"/><path d="M104.047 247.832s125-51.3 168.352-69.364c16.619-7.225 72.977-30.347 72.977-30.347s26.012-10.115 23.844 14.451c-.723 10.116-6.503 45.52-12.283 83.815-8.671 54.191-18.064 113.439-18.064 113.439s-1.445 16.619-13.728 19.509-32.515-10.115-36.127-13.006c-2.891-2.167-54.191-34.682-72.977-50.578-5.058-4.335-10.838-13.005.722-23.121 26.012-23.844 57.081-53.468 75.867-72.254 8.671-8.671 17.341-28.902-18.786-4.336-51.3 35.405-101.878 68.642-101.878 68.642s-11.561 7.225-33.237.722c-21.677-6.502-46.966-15.173-46.966-15.173s-17.34-10.838 12.284-22.399z" fill="#fff"/></svg>
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
"formik": "^2.1.5", "formik": "^2.1.5",
"next": "^9.4.4", "next": "^9.4.4",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"prism-themes": "^1.9.0",
"react": "^16.13.1", "react": "^16.13.1",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"react-tagsinput": "^3.19.0", "react-tagsinput": "^3.19.0",
......
...@@ -15,18 +15,17 @@ import '../styles/variables.css' ...@@ -15,18 +15,17 @@ import '../styles/variables.css'
import '../styles/nprogress.css' import '../styles/nprogress.css'
import 'react-tagsinput/react-tagsinput.css' import 'react-tagsinput/react-tagsinput.css'
import '../styles/app.css' import '../styles/app.css'
import '../styles/dev.css'
import '../styles/automatedanswer.css' import '../styles/automatedanswer.css'
import 'prism-themes/themes/prism-night-owl.min.css'
Router.events.on('routeChangeStart', () => NProgress.start()) Router.events.on('routeChangeStart', () => NProgress.start())
Router.events.on('routeChangeComplete', () => NProgress.done()) Router.events.on('routeChangeComplete', () => NProgress.done())
Router.events.on('routeChangeError', () => NProgress.done()) Router.events.on('routeChangeError', () => NProgress.done())
function MyApp({ Component, pageProps }) { function MyApp({ Component, pageProps }) {
const { const { ref, isComponentVisible, setIsComponentVisible } =
ref, useComponentVisible(false)
isComponentVisible,
setIsComponentVisible
} = useComponentVisible(false)
const [authScreen, setAuthScreen] = useState(null) const [authScreen, setAuthScreen] = useState(null)
......
...@@ -48,6 +48,7 @@ class MyDocument extends Document { ...@@ -48,6 +48,7 @@ class MyDocument extends Document {
{/* favicon */} {/* favicon */}
<link rel="shortcut icon" href="/images/logo.svg" /> <link rel="shortcut icon" href="/images/logo.svg" />
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.2.0/highlight.min.js"></script>
</Head> </Head>
<body> <body>
<Main /> <Main />
......
...@@ -95,7 +95,7 @@ const QuestionDetail = ({ questionId, title }) => { ...@@ -95,7 +95,7 @@ const QuestionDetail = ({ questionId, title }) => {
</PostWrapper> </PostWrapper>
<AutomatedAnswerContainer question_id={questionId} /> <AutomatedAnswerContainer question_id={questionId} />
{question.answers.length > 0 && ( {question.answers.length > 0 && (
<AnswerContainer <AnswerContainer
answersCount={question.answers.length} answersCount={question.answers.length}
......
...@@ -52,12 +52,12 @@ ...@@ -52,12 +52,12 @@
} }
} }
.s-prose *:not(.s-code-block) > code { /* .s-prose *:not(.s-code-block) > code {
padding: 2px 4px; padding: 2px 4px;
color: var(--white); color: var(--white);
background-color: var(--black-600); background-color: var(--black-600);
border-radius: 3px; border-radius: 3px;
} } */
sup, sup,
sub { sub {
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -2,7 +2,7 @@ import axios from 'axios' ...@@ -2,7 +2,7 @@ import axios from 'axios'
const baseURL = const baseURL =
process.env.NODE_ENV === 'development' process.env.NODE_ENV === 'development'
? 'http://localhost:8080/api' ? 'http://localhost:5000/api'
: `https://${process.env.SITE_NAME}/api` : `https://${process.env.SITE_NAME}/api`
const publicFetch = axios.create({ const publicFetch = axios.create({
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment