import * as React from "react"; import {connect} from "react-redux"; import {bindActionCreators, Dispatch} from "redux"; import {returntypeof} from 'react-redux-typescript'; import {RootState} from '../../state/reducers' import {notExhaustive} from '../../state/reducers/_notExhausiveHelper' import {delay, wait} from '../../../debug' import {Icon} from 'semantic-ui-react' export interface MyProps { //readonly test: string } const mapStateToProps = (rootState: RootState /*, props: MyProps*/) => { return { //test0: rootState... //test: props.test } } const mapDispatchToProps = (dispatch: Dispatch<any>) => bindActionCreators({ //imported reducer funcs here }, dispatch) const stateProps = returntypeof(mapStateToProps); const dispatchProps = returntypeof(mapDispatchToProps); type Props = typeof stateProps & typeof dispatchProps; interface ConversationUserEntered { readonly kind: 'user-entered' readonly message: string } interface ConversationMessage { readonly kind: 'message' /** * undefined for own user */ readonly name?: string readonly message: string readonly withLoading: boolean } type ChatMessage = ConversationUserEntered | ConversationMessage interface State { // tslint:disable-next-line readonly currentConversation: ChatMessage[] } const chatWrapperId = 'chat-window-wrapper' class Chat404 extends React.Component<Props, State> { /** * after unmount cancel recursion */ disposed = false /** * a deep copy because we need to modify loading state */ // tslint:disable-next-line conversationCopy = JSON.parse(JSON.stringify(conversion)) as ChatMessage[] mainDiv: HTMLDivElement | null = null constructor(props: Props) { super(props) this.state = { currentConversation: [], } } componentDidMount(): void { this.displayAllMessages() } componentWillUnmount(): void { this.disposed = true } getRndDelay(): number { // tslint:disable-next-line return this.rnd(1000, 2000) } rnd(inclusiveMin: number, inclusiveMax: number): number { return Math.floor(Math.random() * (inclusiveMax + 1 - inclusiveMin)) + inclusiveMin; } async displayAllMessages(): Promise<void> { // tslint:disable-next-line for (let i = 0; i < this.conversationCopy.length; i++) { // tslint:disable-next-line let message = this.conversationCopy[i]; if (this.disposed) return //a message could need to display loading if (message.kind === 'message' && message.withLoading) { this.setState((prevState: State) => { return { currentConversation: [...prevState.currentConversation, message], } as State }) message = { ...message, withLoading: false } as ChatMessage await wait(this.getRndDelay()) //remove old message and insert new with changed props this.setState((prevState: State) => { return { currentConversation: [...prevState.currentConversation.filter( (value, index1) => index1 !== prevState.currentConversation.length - 1), message], } as State }) await wait(this.getRndDelay()) continue } this.setState((prevState: State) => { return { currentConversation: [...prevState.currentConversation, message], } as State }) await wait(this.getRndDelay()) } } render(): JSX.Element { return ( <div className="chat-component"> <div className="watermark"> 404 </div> <div className="down-button clickable" onClick={() => { const scrollables = document.getElementById(chatWrapperId) if (scrollables && this.mainDiv) { scrollables.scrollTo(0, this.mainDiv.scrollHeight) } }}> <Icon name="chevron down"/> </div> <div className="fh fw scrollable" id={chatWrapperId}> <div ref={p => this.mainDiv = p} className="chat-window"> { this.state.currentConversation.map((value, index) => { const key = index switch (value.kind) { case 'user-entered': return (<div key={key} className="chat-entry user-entered">{value.message}</div>) case 'message': if (value.name === undefined) { if (value.withLoading) { return (<div key={key} className="chat-entry own-message is-typing"> <div></div> <div> <span></span> <span></span> <span></span> </div> </div>) } return (<div key={key} className="chat-entry own-message">{value.message}</div>) } if (value.withLoading) { return (<div key={key} className="chat-entry foreign-message is-typing"> <div className={`user-name${value.name === 'Janis' ? ' alternate' : ''}`}>{value.name}</div> <div> <span></span> <span></span> <span></span> </div> </div>) } return (<div key={key} className="chat-entry foreign-message"> <div className={`user-name${value.name === 'Janis' ? ' alternate' : ''}`}>{value.name}</div> <div>{value.message}</div> </div>) default: notExhaustive(value) } }) } </div> </div> </div> ) } } export default connect(mapStateToProps, mapDispatchToProps)(Chat404) const conversion: ReadonlyArray<ChatMessage> = [ { kind: 'user-entered', message: 'Steffen hat den Chat betreten' }, { kind: 'user-entered', message: 'Janis hat den Chat betreten' }, { kind: 'message', message: '???', withLoading: false }, { kind: 'message', name: 'Steffen', withLoading: true, message: 'Hey Janis, ich glaube da hat mal wieder jemand mit der URL rumgespielt. Oder kennst du die geforderte Seite?' }, { kind: 'message', name: 'Janis', withLoading: true, message: 'Hm, komisch. Wie kommt man denn auf sowas?' }, { kind: 'message', name: 'Steffen', withLoading: true, message: 'Vorallem, wer tippt heute noch manuell eine URL ein, oder haben wir irgendwo einen falschen Link gesetzt?' }, { kind: 'message', name: 'Janis', withLoading: true, message: 'Ausgeschlossen, bei YAPEX gibt es keine Fehler!!!1!!11!' }, { kind: 'message', name: 'Steffen', withLoading: true, message: 'Jaja, von wegen keine Fehler. Ich habe dir aber doch gleich gesagt, dass wir einen vernünftigen 404-Seite brauchen!' }, { kind: 'message', name: 'Janis', withLoading: true, message: 'Also wir können doch nicht alles abfangen und eine leere Seite reicht vollkommen aus.' }, { kind: 'message', name: 'Steffen', withLoading: true, message: 'Dann kann man aber nicht erkennen, dass die Seite tatsächlich nicht existiert. Wir brauchen schon irgendeine coole 404-Seite.' }, { kind: 'message', name: 'Janis', withLoading: true, message: 'Na da bin ich aber gespannt.' }, { kind: 'message', name: 'Steffen', withLoading: true, message: 'Es könnten so Binärzahlen runter rieseln, die dann 404 ergeben.' }, { kind: 'message', name: 'Janis', withLoading: true, message: 'Ja klar, geht es noch aufwendiger?' }, { kind: 'message', name: 'Steffen', withLoading: true, message: 'Statt Binärzahlen könnten wir auch Links zu unterschiedlichen Seiten von YAPEX runterfallen lassen, die nach Häufigkeit ihrer Verwendung unterschiedlich groß ausfallen, zuerst das YAPEX-Logo nachbilden und dann in 404 zerfallen.' }, { kind: 'message', name: 'Janis', withLoading: true, message: 'OK, ich mache, sobald ich dazu komme, eine einfache klassische 404-Seite.' }, { kind: 'message', name: 'Steffen', withLoading: true, message: 'Wir könnten auch 404 binär darstellen lassen, dann in unseren Editor anzeigen lassen, in welchem gerade ein Programm zur Umwandlung von Binärzahlen in Dezimalzahlen geschrieben wird und dann ein Testfall ausgeführt wird, der eben 404 liefert! Soll ich ein Ticket erstellen?' }, { kind: 'message', name: 'Janis', withLoading: true, message: 'Noch mehr Wünsche? Du und deine ganzen Tickets, die werden wir eh nie schaffen abzuarbeiten...' }, { kind: 'message', name: 'Steffen', withLoading: true, message: 'Ich kann schließlich nichts dafür immer auf irgendwelche Fehler zu stoßen.' }, { kind: 'message', name: 'Janis', withLoading: true, message: 'Stimmt, das kann nur Talent sein.' }, { kind: 'message', name: 'Steffen', withLoading: true, message: 'War das jetzt ironisch? Also ein paar gute Ideen waren schon dabei und YAPEX ist doch gut gewachsen. Gut, es gibt immer etwas zu tun, und ich hätte doch noch die ein oder andere Idee...' }, { kind: 'message', name: 'Janis', withLoading: true, message: '...und ich darf die immer umsetzen. Wir bräuchten da dringend noch jemand, der mitmacht.' }, { kind: 'message', name: 'Steffen', withLoading: true, message: 'Sag mal, ist besagte Person immer noch auf dieser nicht vorhandenen Seiten? Die hat demnach viel zu viel Zeit, vielleicht sollten wir sie mal anschreiben?!' }, { kind: 'message', name: 'Janis', withLoading: true, message: 'Weiß ich doch nicht. Bestimmt beim Bearbeiten von einer deiner Aufgabe eingeschlafen und mit dem Kopf auf die Tastatur gekommen...' }, { kind: 'message', name: 'Steffen', withLoading: true, message: 'Du meinst ich habe zu leichte und langweilige Aufgaben gestellt? Ich habe auch das Gefühl, dass da noch eine gewisse Herausforderung fehlt.' }, { kind: 'message', name: 'Janis', withLoading: true, message: 'Ich sag dazu jetzt mal lieber nichts...' }, { kind: 'message', name: 'Steffen', withLoading: true, message: 'Ach komm. Da fällt mir ein, ich könnte mal einen einigermaßen intelligenten Chatbot in Java programmieren lassen, nur müssten wir unsere Tests etwas anpassen.' }, { kind: 'message', name: 'Janis', withLoading: true, message: 'Das kann dann aber jemand anderes umsetzen.' }, { kind: 'message', name: 'Steffen', withLoading: true, message: 'Das ist bestimmt total einfach.' }, { kind: 'message', name: 'Janis', withLoading: true, message: 'Natürlich... total einfach, weißt du was ich da noch alles ändern muss? Momentan geht das so sowieso nicht. Und wer will schon so eine Aufgabe bearbeiten?' }, { kind: 'message', name: 'Steffen', withLoading: true, message: 'Hrm, zeitlich sieht es bei mir sowieso eng aus. Aber ich kann das mal im Hinterkopf behalten.' }, { kind: 'message', name: 'Janis', withLoading: true, message: 'Also machen wir jetzt einfach eine simple 404-Seite?' }, { kind: 'message', name: 'Steffen', withLoading: true, message: 'Gut, aber nur als Platzhalter, bis wir mal Zeit finden eine bessere Seite zu erstellen.' }, { kind: 'message', name: 'Janis', withLoading: true, message: 'Super, find ich gut.' } ]