dbrgn/pyxtra 11
A small commandline utility written in Python to access the (now dead) Swisscom Xtrazone SMS service
petermanser/Jira-cards-printing 2
Print Jira stories in your own formatted way
petermanser/Analysis-Formelsammlung 1
Analysis Formelsammlung basierend auf dem Schulstoff der Hochschule für Technik Rapperswil (HSR)
petermanser/emoji-cheat-sheet.com 1
A one pager for emojis on Campfire and GitHub
A microframework based on Werkzeug, Jinja2 and good intentions
Provides flat static pages to a Flask application
A GitHub mirror of flask-login
An example template app, showing the use of Backbone.js and Zepto.js in a Forge app.
petermanser/gesagt-im-parlament.ch 1
A swiss parliament related project built during #MakeOpenData
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
{- "name": "cas-fee-adv-qwacker-tpl",- "version": "0.0.0-development",+ "name": "app-yeahyeahyeah",+ "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start",- "lint": "next lint"+ "lint": "next lint",+ "lint:fix": "next lint --fix",+ "format:check": "prettier --check {data,hooks,services,src}/**/*.{js,jsx,ts,tsx,css,md,json} --config ./.prettierrc",
prettier --check 'src/**/*.{ts,tsx}' 'data/*.json' --config ./.prettierrc
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+FROM node:16-alpine+ARG NPM_TOKEN+WORKDIR /usr/src/app+RUN npm install --global pm2+COPY .npmrc ./+RUN echo "//npm.pkg.github.com/:_authToken=${NPM_TOKEN}" >> .npmrc+COPY package*.json ./+RUN npm install
npm ci
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
/** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true,+ transpilePackages: ['@acme/ui', 'lodash-es'], swcMinify: true,-}+ images: {+ remotePatterns: [+ {+ protocol: 'https',+ hostname: 'storage.googleapis.com',+ port: '',+ pathname: '/qwacker-api-prod-data/**',+ },+ ],+ minimumCacheTTL: 1500000,+ },+ env: {+ NEXT_PUBLIC_URL: process.env.NEXT_PUBLIC_URL,+ },+}; -module.exports = nextConfig+module.exports = nextConfig;
<CHECK>
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import type { NextApiRequest, NextApiResponse } from 'next';++type Data = {+ name: string;+};++export default function handler(req: NextApiRequest, res: NextApiResponse<Data>) {+ const { method, query } = req;+ console.info({ method, query });
console.log
wurde wohl zu Debug zwecken eingeführt. Könnte wohl entfernt werden.
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import axios from 'axios';+import { transformMumble, QwackerMumbleResponse } from '../types/qwacker';++export const fetchMumbles = async (params: {+ limit?: number;+ offset?: number;+ newerThanMumbleId?: string;+ token?: string;+}) => {+ const { limit, offset, newerThanMumbleId, token } = params || {};++ let searchParams = new URLSearchParams({+ limit: limit?.toString() || '10',+ offset: offset?.toString() || '0',+ });++ if (newerThanMumbleId)+ searchParams = new URLSearchParams({+ newerThan: newerThanMumbleId,+ });++ const url = `${process.env.NEXT_PUBLIC_QWACKER_API_URL}/posts?${searchParams}`;++ try {+ const { data, count } = (+ await axios.get(url, {+ headers: {
Ein Helper für die Headers und weiteren Elementen wäre sinnvoll
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import React from 'react';+import Link from 'next/link';+import tw, { styled } from 'twin.macro';+import Message from '../../../data/content.json';+import useSWR from 'swr';+import { useSession } from 'next-auth/react';+import { elapsedTime } from '@/utils/timeConverter';+import { fetchUser, Mumble, QwackerUserResponse } from '@/services';+import {+ Avatar,+ CommentButton,+ Cancel,+ IconLink,+ User,+ Paragraph,+} from '@smartive-education/design-system-component-library-yeahyeahyeah';+import { Like } from './Like';+import { Share } from './Share';+import { Picture } from './Picture';+import { renderHashtags } from './Hashtag';++type MumbleProps = {+ type: string;+ $isReply?: boolean;+ handleDeleteCallback?: (id: string) => void;+ fallbackUsers?: QwackerUserResponse;+} & Mumble;++export const Post: React.FC<MumbleProps> = ({+ id,+ creator,+ text,+ mediaUrl,+ createdTimestamp,+ likeCount,+ likedByUser,+ replyCount,+ type,+ $isReply,+ handleDeleteCallback,+ fallbackUsers,+}) => {+ const { data: session }: any = useSession();+ const { data } = useSWR(
Wieso ladet ihr hier den User nochmals? Das bringt unnötige Komplexität und Ladezyklen.
Wenn ich demo-mässig diese Zeile ersetze durch:
const data = fallbackUsers?.data?.find((x) => x.id === creator);
... sehe ich keine Änderung.
Diese Komponente sollte keine Datenlogik mehr haben, sondern nur die Daten rendern.
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import React from 'react';+import { FetchMumbles } from '@/types/fallback';+import { Button, Container, Heading } from '@smartive-education/design-system-component-library-yeahyeahyeah';+import { KeyedMutator } from 'swr';+import tw from 'twin.macro';+import { WelcomeText } from '../content/WelcomeText';+import { Alert } from '../alert/Alert';+import { TextBoxComponent } from '../form/TextBoxComponent';+import { Hashtag } from './Hashtag';+import { User } from '@/services';++type TimelineProps = {+ mutate: KeyedMutator<FetchMumbles[]>;+ checkForNewMumbles: boolean;+ quantityNewMumbles: string;+ handleRefreshPage: () => void;+ renderMumbles: () => JSX.Element;+ hashtag?: string;+ creator?: { id: string };+ fallbackUserLoggedIn?: User;+};++export const Timeline: React.FC<TimelineProps> = ({+ mutate,+ checkForNewMumbles,+ quantityNewMumbles,+ handleRefreshPage,+ renderMumbles,+ hashtag,+ creator,+ fallbackUserLoggedIn,+}) => {+ return (+ <>+ {!hashtag && checkForNewMumbles && (+ <MumbleMessageBox>+ <Button label={quantityNewMumbles} color="gradient" onClick={handleRefreshPage} size="small" width="full" />+ </MumbleMessageBox>+ )}+ <Container layout="plain">+ {!hashtag && !creator && (+ <>+ <WelcomeText />+ <Alert />+ <TextBoxComponent variant="write" mutate={mutate} fallbackUserLoggedIn={fallbackUserLoggedIn} />+ </>+ )}+ {hashtag && (
Gehört wohl auch auf die hashtag
Page, da es ja nur da genutzt wird.
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import React, { useState } from 'react';+import { GetServerSideProps, GetServerSidePropsContext } from 'next';+import { NextSeo } from 'next-seo';+import tw from 'twin.macro';+import Message from '../../../data/content.json';+import { getToken } from 'next-auth/jwt';+import { useSession } from 'next-auth/react';+import { fetchMyLikes, fetchMyMumbles, fetchUser, fetchUsers, QwackerUserResponse, User } from '@/services';+import { FetchMumbles } from '@/types/fallback';+import { Header, Stream } from '@/components';+import { Container, Switch } from '@smartive-education/design-system-component-library-yeahyeahyeah';++type HeaderProps = {+ creator: any;
wieso nicht getyped? (any
)
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import React from 'react';+import { FetchMumbles } from '@/types/fallback';+import { useStream } from '@/hooks';+import { MumbleFetcher, SearchMumblesFetcher } from '@/types/swr';+import { Listing } from '../mumble/Listing';+import { LoadingSpinner } from '../loading/LoadingSpinner';+import { Timeline } from '../mumble/Timeline';+import { TextBoxComponent } from '../form/TextBoxComponent';+import { Alert } from '../alert/Alert';+import { QwackerUserResponse, User, alertService } from '@/services';++type StreamProps = {+ url: string;+ limit: number;+ fallback: FetchMumbles;+ fallbackUsers?: QwackerUserResponse;+ fallbackUserLoggedIn?: User;+ fetcher: MumbleFetcher | SearchMumblesFetcher;+ id?: string;+ hashtag?: string;+ creator?: { id: string };+};++export const Stream: React.FC<StreamProps> = ({+ limit,+ fallback,+ hashtag,+ fetcher,+ creator,+ url,+ id,+ fallbackUsers,+ fallbackUserLoggedIn,+}) => {+ const [+ data,+ mutate,+ error,+ isValidating,+ isLoading,+ checkForNewMumbles,+ quantityNewMumbles,+ renderTimeline,+ handleDelete,+ handleRefreshPage,+ ref,+ ] = useStream(url, limit, fallback, fetcher, id, hashtag, creator);++ const renderMumbles = (isReply?: boolean) => {+ return (+ <>+ {data && <Listing data={data} handleDelete={handleDelete} isReply={isReply} fallbackUsers={fallbackUsers} />}+ <div key="last" tw="invisible" ref={ref} />+ <div tw="h-16 mb-32">{(isLoading || isValidating) && <LoadingSpinner />}</div>+ </>+ );+ };++ if (error) {+ alertService.error(`${error}`, {+ autoClose: false,+ keepAfterRouteChange: false,+ });+ return <Alert />;+ }++ return (+ <>+ {renderTimeline ? (
Verstehe hier die Logik nicht ganz: Was ist der Unterschied zwischen Timeline
, renderMumbles(true)
und renderMumbles()
(Entscheidung basierend auf id
).
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import React from 'react';+import { FetchMumbles } from '@/types/fallback';+import { useStream } from '@/hooks';+import { MumbleFetcher, SearchMumblesFetcher } from '@/types/swr';+import { Listing } from '../mumble/Listing';+import { LoadingSpinner } from '../loading/LoadingSpinner';+import { Timeline } from '../mumble/Timeline';+import { TextBoxComponent } from '../form/TextBoxComponent';+import { Alert } from '../alert/Alert';+import { QwackerUserResponse, User, alertService } from '@/services';++type StreamProps = {+ url: string;+ limit: number;+ fallback: FetchMumbles;+ fallbackUsers?: QwackerUserResponse;+ fallbackUserLoggedIn?: User;+ fetcher: MumbleFetcher | SearchMumblesFetcher;+ id?: string;+ hashtag?: string;+ creator?: { id: string };+};++export const Stream: React.FC<StreamProps> = ({+ limit,+ fallback,+ hashtag,+ fetcher,+ creator,+ url,+ id,+ fallbackUsers,+ fallbackUserLoggedIn,+}) => {+ const [+ data,+ mutate,+ error,+ isValidating,+ isLoading,+ checkForNewMumbles,+ quantityNewMumbles,+ renderTimeline,+ handleDelete,+ handleRefreshPage,+ ref,+ ] = useStream(url, limit, fallback, fetcher, id, hashtag, creator);++ const renderMumbles = (isReply?: boolean) => {
wieso nutzt ihr nicht den type
auf dem Mumble? Dieses Property braucht es nicht.
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import axios from 'axios';+import { transformMumble, QwackerMumbleResponse } from '../types/qwacker';++export const fetchMumbles = async (params: {+ limit?: number;+ offset?: number;+ newerThanMumbleId?: string;+ token?: string;+}) => {+ const { limit, offset, newerThanMumbleId, token } = params || {};++ let searchParams = new URLSearchParams({+ limit: limit?.toString() || '10',+ offset: offset?.toString() || '0',+ });++ if (newerThanMumbleId)+ searchParams = new URLSearchParams({
searchParams.append('newerThan', newerThanMumbleId);
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import { UploadImage } from '@/services';++type TextBoxState = {+ errorMessage: string;+ fileUploadError: string;+ file: UploadImage | null;+ showModal: boolean;+ inputValue: string;+};++type TextBoxAction = {
Hier würden sich Union Types anbieten: So können keine falschen Types/Kombis übergeben werden.
type TextBoxAction =
| { type: 'SET_ERROR_MESSAGE'; payload: string }
| { type: 'CLEAR_ERROR_MESSAGE' }
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import type { NextApiRequest, NextApiResponse } from 'next';++type Data = {+ name: string;+};++export default function handler(req: NextApiRequest, res: NextApiResponse<Data>) {
braucht ihr dieses File?
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
-This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).+ -## Getting Started+# Mumble Chat App -First, run the development server:+## Introduction++This is the final project for the 2nd part of the **Frontend Engineering Advanced** course, offered by the **University Ost** in Rapperswil.++We had to develop a **Chat App** based on [NextJS](https://nextjs.org) in a given **Mumble Design** according to the specification of [Mumble-Figma-Design](https://www.figma.com/file/nsXR2h0KwciWpuwKRD58FX/Mumble?node-id=437-1018). We had the requirement to use our previously developed [Component Library](https://github.com/smartive-education/design-system-component-library-yeahyeahyeah) for our **Chat App**.++## Table of contents++- [Getting started](#getting-started)+- [Add Credentials](#add-credentials)+- [Add .env vars](#add-env-vars)+- [Install dependencies](#install-dependencies)+- [Resources](#resources)++## Getting started++In the next steps you will setup the chat app.++### Prerequisites++#### NODE++Please use node version **16.19.1**. If you use _nvm_ you can use the following command.++```shell+nvm use 16.19.1+```++## Add credentials++We need a github token and a .npmrc to get access to the [mumble npm package](https://github.com/smartive-education/design-system-component-library-yeahyeahyeah/pkgs/npm/design-system-component-library-yeahyeahyeah) at [smartive education](https://github.com/smartive-education) on github.++Create github token and add to .npmrc++[Create a classic github token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token#creating-a-personal-access-token-classic)++To authenticate by adding your personal access token (classic) to your ~/.npmrc file, edit the ~/.npmrc file for your project to include the following line, replacing TOKEN with your personal access token. Create a new ~/.npmrc file if one doesn’t exist.++```bash+//npm.pkg.github.com/:_authToken=TOKEN+```++Create .npmrc in the project folder, where you wanna add your npm package and add following line++```bash+@smartive-education:registry=https://npm.pkg.github.com+```++## Add .env vars++Create a new _.env_ file in the root directory. I have sent you an email with the _.env_ vars. Please copy them into the newly created _.env_ file.++Now, you should be able to start the application.++## Installation++```bash+git clone https://github.com/smartive-education/app-yeahyeahyeah.git++cd app-yeahyeahyeah++npm install
npm ci
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import React from 'react';+import { GetServerSideProps } from 'next';+import Message from '../../../data/content.json';+import { getToken } from 'next-auth/jwt';+import { FetchMumbles } from '@/types/fallback';+import { QwackerUserResponse, fetchUsers, searchMumbles } from '@/services';+import { Stream } from '@/components/stream/Stream';+import { NextSeo } from 'next-seo';++const HashtagPage = ({+ limit,+ fallback,+ fallbackUsers,+ hashtag,+}: {+ limit: number;+ fallback: FetchMumbles;+ fallbackUsers: QwackerUserResponse;+ hashtag: string;+}) => {+ return (+ <>+ <NextSeo title={`${Message.seo.search.title}`} description={`${Message.seo.search.description}`} />+ <Stream+ url="/api/mumbles"+ limit={limit}+ fallback={fallback}+ fallbackUsers={fallbackUsers}+ hashtag={hashtag}+ fetcher={searchMumbles}+ />+ </>+ );+};+export const getServerSideProps: GetServerSideProps<any> = async ({ req, query: { hashtag } }) => {+ const limit = 2;+ const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET });+ const mumbles: FetchMumbles = await searchMumbles({
Hier würde sich eine Auslagerung anbieten in einen Custom Hook useSearchMumbles
. Oder sogar den Stream verwenden, welche ja auch weitere Logik wie new mumbles/load more hat.
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import React, { useState } from 'react';+import { GetServerSideProps, GetServerSidePropsContext } from 'next';+import { NextSeo } from 'next-seo';+import tw from 'twin.macro';+import Message from '../../../data/content.json';+import { getToken } from 'next-auth/jwt';+import { useSession } from 'next-auth/react';+import { fetchMyLikes, fetchMyMumbles, fetchUser, fetchUsers, QwackerUserResponse, User } from '@/services';+import { FetchMumbles } from '@/types/fallback';+import { Header, Stream } from '@/components';+import { Container, Switch } from '@smartive-education/design-system-component-library-yeahyeahyeah';++type HeaderProps = {+ creator: any;+ limit: number;+ fallbackUser: User;+ fallbackUsers: QwackerUserResponse;+ fallBackMyMumbles: FetchMumbles;+ fallBackMyLikes: FetchMumbles;+};++const ProfilePage = ({ creator, limit, fallbackUser, fallBackMyMumbles, fallBackMyLikes, fallbackUsers }: HeaderProps) => {+ const { data: session }: any = useSession();+ const [selection, setSelection] = useState('mumbles');++ const handleSelection = (value: string) => {+ setSelection(value);+ };++ return (+ <>+ <NextSeo+ title={`${session && session.user.firstname} ${session && session.user.lastname}'s mumble profile`}+ description={`Das Mumble-Profile von ${+ session && session.user.username+ }. Mumble, die Chat-App des CAS Frontend Engineer Advanced 2023.`}+ canonical={process.env.NEXT_PUBLIC_URL}+ />++ <Container layout="plain">+ <Header creator={creator} fallbackUser={fallbackUser} />++ {session?.user.id === creator.id ? (
hier würde es sich anbieten, das etwas auszulagern. ihr habt hier sehr verschachtelte Komponenten, was die Lesbarkeit schlechter macht.
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import React from 'react';+import { GetServerSideProps } from 'next';+import Message from '../../../data/content.json';+import { getToken } from 'next-auth/jwt';+import { FetchMumbles } from '@/types/fallback';+import { QwackerUserResponse, fetchUsers, searchMumbles } from '@/services';+import { Stream } from '@/components/stream/Stream';+import { NextSeo } from 'next-seo';++const HashtagPage = ({+ limit,+ fallback,+ fallbackUsers,+ hashtag,+}: {+ limit: number;+ fallback: FetchMumbles;+ fallbackUsers: QwackerUserResponse;+ hashtag: string;+}) => {+ return (+ <>+ <NextSeo title={`${Message.seo.search.title}`} description={`${Message.seo.search.description}`} />+ <Stream+ url="/api/mumbles"+ limit={limit}+ fallback={fallback}+ fallbackUsers={fallbackUsers}+ hashtag={hashtag}+ fetcher={searchMumbles}+ />+ </>+ );+};+export const getServerSideProps: GetServerSideProps<any> = async ({ req, query: { hashtag } }) => {+ const limit = 2;
Solche Konstanten würden sich als env Variablen eignen, um das global einstellen zu können.
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import axios from 'axios';++export const deleteMumble = async (id: string, accessToken?: string) => {+ try {+ const response = await axios.delete(`${process.env.NEXT_PUBLIC_QWACKER_API_URL}/posts/${id}`, {+ headers: {+ Authorization: accessToken ? `Bearer ${accessToken}` : null,+ },+ });++ if (!response) {+ throw new Error('Something was not okay');
Hier würde sich eine Custom Error Class anbieten:
class DeleteMumbleError extends Error {
constructor(message: string) {
super(message);
this.name = 'DeleteMumbleError';
}
}
So könntet ihr später den Fehler einfacher catchen.
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import { useEffect, useRef } from 'react';+import { useSession } from 'next-auth/react';+import useOnScreen from '@/hooks/useOnScreen';+import useSWR from 'swr';+import useSWRInfinite from 'swr/infinite';+import { alertService, deleteMumble } from '@/services';+import { FetchMumbles } from '@/types/fallback';+import { MumbleFetcher, SearchMumblesFetcher, StreamHook } from '@/types/swr';+import debounce from 'lodash.debounce';++export function useStream(+ url: string,+ limit: number,+ fallback: FetchMumbles,+ fetcher: MumbleFetcher | SearchMumblesFetcher,+ id?: string,+ hashtag?: string,+ creator?: { id: string }+) {+ const { data: session }: any = useSession();+ const ref = useRef<HTMLDivElement>(null);+ const [isOnScreen, setIsOnScreen] = useOnScreen(ref);++ const getKey = (pageIndex: number, previousPageData: FetchMumbles) => {+ if (previousPageData && !previousPageData.mumbles.length) {+ return null;+ }++ return {+ id,+ url,+ limit,+ offset: pageIndex * limit,+ token: session?.accessToken,+ tags: [hashtag],+ creator: creator?.id,+ };+ };++ const { data, mutate, size, setSize, error, isValidating, isLoading } = useSWRInfinite(getKey, fetcher, {+ fallbackData: [fallback],+ revalidateOnFocus: false,+ refreshInterval: 60000,+ // Hint: For better user experience, set this to true (more requests especially if small limit is set)+ revalidateAll: false,+ parallel: true,+ });++ const { data: newMumbles } = useSWR(+ id || hashtag || creator+ ? null+ : {+ url: '/api/mumbles',+ newerThanMumbleId: data && data[0]?.mumbles[0]?.id,+ limit,+ offset: 0,+ token: session?.accessToken,+ },+ fetcher,+ {+ revalidateOnFocus: false,+ refreshInterval: 10000,+ }+ );++ const handleIntersectionCallbackDebounced = debounce(async () => {+ setSize(size + 1);+ setIsOnScreen(false);+ }, 200);++ useEffect(() => {+ // TODO: id is needed on profile page, because there is no possibility for setting offset and limit on endpoint+ if (!id && isOnScreen && !isValidating && data && data.length * limit <= data[0].count)+ handleIntersectionCallbackDebounced();+ });++ const checkForNewMumbles = data && data[0]?.mumbles[0]?.id && newMumbles && newMumbles.count > 0;++ const quantityNewMumbles =+ data && data[0]?.mumbles[0]?.id && newMumbles && newMumbles.count === 1+ ? '1 neuer Mumble'+ : `${newMumbles && newMumbles.count} neue Mumbles`;++ const renderTimeline = !creator && !id;++ const handleDelete = async (id: string) => {+ if (!session?.accessToken) {+ alertService.error('Bitte melde dich an, sonst kannst du nicht löschen!!', {+ autoClose: true,+ keepAfterRouteChange: false,+ });+ return;+ }+ const res = await deleteMumble(id, session?.accessToken);++ //TODO: Is this a magic number, i don't think so.
Nein, ist der Status Code für "Sucess", aber "No Content": https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+// pages/_document.tsx+import React from 'react';+import Document, { Html, DocumentContext, NextScript } from 'next/document';+import { ServerStyleSheet } from 'styled-components';++export default class MyDocument extends Document {+ static async getInitialProps(ctx: DocumentContext) {+ const sheet = new ServerStyleSheet();+ const originalRenderPage = ctx.renderPage;+ try {+ ctx.renderPage = () =>+ originalRenderPage({+ enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />),+ });+ const initialProps = await Document.getInitialProps(ctx);++ return {+ ...initialProps,+ styles: [+ <React.Fragment key="styles">+ <Html lang="de">
Schaut hier: https://nextjs.org/docs/advanced-features/custom-document#customizing-renderpage
Das Html
und seine untergeordneten Elemente sollten ausserhalb des styles
Arrays sein. Diese können innerhalb der render
Methode platziert werden:
static async getInitialProps(ctx: DocumentContext) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<React.Fragment key="styles">
{initialProps.styles}
{sheet.getStyleElement()}
</React.Fragment>
),
};
} finally {
sheet.seal();
}
}
render() {
return (
<Html lang="de">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import React, { useState } from 'react';+import { GetServerSideProps, GetServerSidePropsContext } from 'next';+import { NextSeo } from 'next-seo';+import tw from 'twin.macro';+import Message from '../../../data/content.json';+import { getToken } from 'next-auth/jwt';+import { useSession } from 'next-auth/react';+import { fetchMyLikes, fetchMyMumbles, fetchUser, fetchUsers, QwackerUserResponse, User } from '@/services';+import { FetchMumbles } from '@/types/fallback';+import { Header, Stream } from '@/components';+import { Container, Switch } from '@smartive-education/design-system-component-library-yeahyeahyeah';++type HeaderProps = {+ creator: any;+ limit: number;+ fallbackUser: User;+ fallbackUsers: QwackerUserResponse;+ fallBackMyMumbles: FetchMumbles;+ fallBackMyLikes: FetchMumbles;+};++const ProfilePage = ({ creator, limit, fallbackUser, fallBackMyMumbles, fallBackMyLikes, fallbackUsers }: HeaderProps) => {+ const { data: session }: any = useSession();+ const [selection, setSelection] = useState('mumbles');++ const handleSelection = (value: string) => {+ setSelection(value);+ };++ return (+ <>+ <NextSeo+ title={`${session && session.user.firstname} ${session && session.user.lastname}'s mumble profile`}+ description={`Das Mumble-Profile von ${+ session && session.user.username+ }. Mumble, die Chat-App des CAS Frontend Engineer Advanced 2023.`}+ canonical={process.env.NEXT_PUBLIC_URL}+ />++ <Container layout="plain">+ <Header creator={creator} fallbackUser={fallbackUser} />++ {session?.user.id === creator.id ? (+ <>+ <SwitchContentWrapper>+ <Switch+ fCallBack={(value) => handleSelection(value)}+ options={[+ {+ label: `${Message.contents.switch.mumbles}`,+ value: 'mumbles',+ },+ {+ label: `${Message.contents.switch.likes}`,+ value: 'likes',+ },+ ]}+ value="mumbles"+ />+ </SwitchContentWrapper>++ <SelectionWrapper>+ {selection === 'mumbles' && (+ <Stream+ url="/api/myMumbles"+ limit={limit}+ fallback={fallBackMyMumbles}+ fallbackUsers={fallbackUsers}+ fetcher={fetchMyMumbles}+ creator={creator}+ />+ )}+ {selection === 'likes' && (+ <Stream+ url="/api/myLikes"+ // TODO: limit is set to 100 because we have to intercept the data (missing endpoint for likes in the API)+ limit={100}+ fallback={fallBackMyLikes}+ fallbackUsers={fallbackUsers}+ fetcher={fetchMyLikes}+ creator={creator}+ />+ )}+ </SelectionWrapper>+ </>+ ) : (+ <SelectionWrapper>+ {selection === 'mumbles' && (+ <Stream+ url="/api/myMumbles"+ limit={limit}+ fallback={fallBackMyMumbles}+ fallbackUsers={fallbackUsers}+ fetcher={fetchMyMumbles}+ creator={creator}+ />+ )}+ </SelectionWrapper>+ )}+ </Container>+ </>+ );+};++export const getServerSideProps: GetServerSideProps<any> = async ({ req, query: { id } }: GetServerSidePropsContext) => {
Wenn ihr hier den Generic nutzt, bitte nicht any
. Könnte gut getyped werden.
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import React from 'react';+import { GetServerSideProps } from 'next';+import Message from '../../../data/content.json';+import { getToken } from 'next-auth/jwt';+import { FetchMumbles } from '@/types/fallback';+import { QwackerUserResponse, fetchUsers, searchMumbles } from '@/services';+import { Stream } from '@/components/stream/Stream';+import { NextSeo } from 'next-seo';++const HashtagPage = ({+ limit,+ fallback,+ fallbackUsers,+ hashtag,+}: {+ limit: number;+ fallback: FetchMumbles;+ fallbackUsers: QwackerUserResponse;+ hashtag: string;+}) => {+ return (+ <>+ <NextSeo title={`${Message.seo.search.title}`} description={`${Message.seo.search.description}`} />+ <Stream+ url="/api/mumbles"+ limit={limit}+ fallback={fallback}+ fallbackUsers={fallbackUsers}+ hashtag={hashtag}+ fetcher={searchMumbles}+ />+ </>+ );+};+export const getServerSideProps: GetServerSideProps<any> = async ({ req, query: { hashtag } }) => {
Wieso hier nicht den Typ von oben (wieder)verwenden?
type HashtagPageProps = {
limit: number;
fallback: FetchMumbles;
fallbackUsers: QwackerUserResponse;
hashtag: string;
};
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+// pages/_app.tsx+import React, { useEffect } from 'react';+import { SessionProvider } from 'next-auth/react';+import { AppProps } from 'next/app';+import GlobalStyles from '@/styles/GlobalStyles';+import { DefaultLayout } from '../layouts';+import { useRouter } from 'next/router';++const App = ({ Component, pageProps: { session, ...pageProps } }: AppProps) => {+ const router = useRouter();++ useEffect(() => {
Es wäre gut hier beispielsweise einen Kommentar hinzuzufügen: Was macht das und wieso wurde es genutzt:
// Disable default scroll restoration behaviour to ...
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import { useEffect, useRef } from 'react';+import { useSession } from 'next-auth/react';+import useOnScreen from '@/hooks/useOnScreen';+import useSWR from 'swr';+import useSWRInfinite from 'swr/infinite';+import { alertService, deleteMumble } from '@/services';+import { FetchMumbles } from '@/types/fallback';+import { MumbleFetcher, SearchMumblesFetcher, StreamHook } from '@/types/swr';+import debounce from 'lodash.debounce';++export function useStream(+ url: string,+ limit: number,+ fallback: FetchMumbles,+ fetcher: MumbleFetcher | SearchMumblesFetcher,+ id?: string,+ hashtag?: string,+ creator?: { id: string }+) {+ const { data: session }: any = useSession();+ const ref = useRef<HTMLDivElement>(null);+ const [isOnScreen, setIsOnScreen] = useOnScreen(ref);++ const getKey = (pageIndex: number, previousPageData: FetchMumbles) => {+ if (previousPageData && !previousPageData.mumbles.length) {+ return null;+ }++ return {+ id,+ url,+ limit,+ offset: pageIndex * limit,+ token: session?.accessToken,+ tags: [hashtag],+ creator: creator?.id,+ };+ };++ const { data, mutate, size, setSize, error, isValidating, isLoading } = useSWRInfinite(getKey, fetcher, {+ fallbackData: [fallback],+ revalidateOnFocus: false,+ refreshInterval: 60000,+ // Hint: For better user experience, set this to true (more requests especially if small limit is set)+ revalidateAll: false,+ parallel: true,+ });++ const { data: newMumbles } = useSWR(
Das ganze "newMumbles" Thema solltet ihr ebenfalls in eine eigene Komponente auslagern.
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import React from 'react';+import Link from 'next/link';+import useSWR from 'swr';+import { Hashtag as HashtagComponent } from '@smartive-education/design-system-component-library-yeahyeahyeah';+import { Mumble, searchMumbles } from '@/services';+import { useSession } from 'next-auth/react';++type HashtagSize = 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge';++type HashtagProps = {+ size: HashtagSize;+ hashtag: string;+};++export const Hashtag: React.FC<HashtagProps> = ({ size, hashtag }) => {+ const { data: session }: any = useSession();++ const { data: hashtagData } = useSWR(+ { url: '/api/mumbles', limit: 10, offset: 0, text: '#', token: session?.accessToken },+ searchMumbles,+ {+ refreshInterval: 10000,+ }+ );++ return <>{hashtagData && hashtagData.mumbles.map((mumble: Mumble) => renderHashtags(mumble.text, size, hashtag))}</>;+};++export const renderHashtags = (text: string, size: HashtagSize, hashtag?: string) => {+ const color = (str: string) => {+ if (hashtag) return str.replace('#', '') === hashtag ? 'violet' : 'slate-300';+ return 'violet';+ };++ return text.split(' ').map((str, i) => {
Persönliche Meinung str
ist kein sprechenger Name, wäre nicht hashtagText
oder textPart
besser?
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
/** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true,+ transpilePackages: ['@acme/ui', 'lodash-es'], swcMinify: true,-}+ images: {+ remotePatterns: [+ {+ protocol: 'https',+ hostname: 'storage.googleapis.com',+ port: '',+ pathname: '/qwacker-api-prod-data/**',+ },+ ],+ minimumCacheTTL: 1500000,+ },+ env: {+ NEXT_PUBLIC_URL: process.env.NEXT_PUBLIC_URL,+ },+}; -module.exports = nextConfig+module.exports = nextConfig;
Wird nachher überschrieben
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import React from 'react';+import Link from 'next/link';+import useSWR from 'swr';+import { Hashtag as HashtagComponent } from '@smartive-education/design-system-component-library-yeahyeahyeah';+import { Mumble, searchMumbles } from '@/services';+import { useSession } from 'next-auth/react';++type HashtagSize = 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge';++type HashtagProps = {+ size: HashtagSize;+ hashtag: string;+};++export const Hashtag: React.FC<HashtagProps> = ({ size, hashtag }) => {+ const { data: session }: any = useSession();++ const { data: hashtagData } = useSWR(+ { url: '/api/mumbles', limit: 10, offset: 0, text: '#', token: session?.accessToken },+ searchMumbles,+ {+ refreshInterval: 10000,+ }+ );++ return <>{hashtagData && hashtagData.mumbles.map((mumble: Mumble) => renderHashtags(mumble.text, size, hashtag))}</>;
Syntatic sugar:
return (
<>
{hashtagData &&
hashtagData.mumbles.map(({ text }: Mumble) =>
renderHashtags(text, size, hashtag)
)}
</>
);
Destructing ist bei so Themen hilfreich: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import React from 'react';+import Link from 'next/link';+import useSWR from 'swr';+import { Hashtag as HashtagComponent } from '@smartive-education/design-system-component-library-yeahyeahyeah';+import { Mumble, searchMumbles } from '@/services';+import { useSession } from 'next-auth/react';++type HashtagSize = 'small' | 'medium' | 'large' | 'xlarge' | 'xxlarge';++type HashtagProps = {+ size: HashtagSize;+ hashtag: string;+};++export const Hashtag: React.FC<HashtagProps> = ({ size, hashtag }) => {+ const { data: session }: any = useSession();++ const { data: hashtagData } = useSWR(+ { url: '/api/mumbles', limit: 10, offset: 0, text: '#', token: session?.accessToken },+ searchMumbles,+ {+ refreshInterval: 10000,+ }+ );++ return <>{hashtagData && hashtagData.mumbles.map((mumble: Mumble) => renderHashtags(mumble.text, size, hashtag))}</>;+};++export const renderHashtags = (text: string, size: HashtagSize, hashtag?: string) => {+ const color = (str: string) => {
const color = (str: string) => hashtag && str.replace("#", "") === hashtag ? "violet" : "slate-300";
(statt ein extra if
)
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import { StartScreen } from '@/components';++const LandingPage: React.FC = () => {
Syntatic sugar:
const LandingPage: React.FC = () => <StartScreen />;
comment created time in a month
Pull request review commentsmartive-education/app-yeahyeahyeah
2023-04-25: Feedback zur Abgabe
+import tw from 'twin.macro';+import Message from '../../data/content.json';+import { Heading, Paragraph } from '@smartive-education/design-system-component-library-yeahyeahyeah';++const InternalServerError: React.FC = () => {+ return (+ <Container>+ <Heading+ label={`${Message.alerts.internalServerError.title}`}+ size="default"+ color="pink"+ tag="h3"+ alignment="center"+ mbSpacing="0"+ />+ <Paragraph size="large" alignment="center">+ {`${Message.alerts.internalServerError.text}`}
Auch hier keine Template literals:
{Message.alerts.internalServerError.text}
comment created time in a month