问:
How to Go Back to Login Page after Logged Out React Native Navigation
I am learning React Native and Redux and building a project.
I have a Profile Page where user can log out. I want to redirect the page to Login after logged out. but I receive below error.
"The action 'NAVIGATE' with payload {"name":"Login"} was not handled by any navigator.
Do you have a screen named 'Login'?"

Navigation is built by someone else in this project so I cant change the structure.
Basically in App.js there is structure checking if the user has the authToken or not. if the user does not have it, login page appears and it is outside of the RootNavigation. if the user has authToken, they can see the homepage which is inside of RootNavigation. The problem is that. Login component is outside of the RootNavigation so it doesnt catch it. How can i solve this problem and navigate to login without breaking this structure ?

Below are the codes.

```<code>App.js

import { StyleSheet, Text, View } from "react-native";
import store from "./src/Redux/store";
import Login from "./src/Components/Login/Login";
import Profile from "./src/Components/Profile/Profile";
import ProjectList from "./src/Components/ProjectList/ProjectList";
import { Provider, useSelector } from "react-redux";
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import Menu from "./src/Components/Menu/Menu";
import ProfileSettings from "./src/Components/ProfileSettings/ProfileSettings"

const Stack = createStackNavigator();
console.log(store.getState())

const AuthStack = () => {
  return (
    <Stack.Navigator screenOptions={{ headerShown: false }}>
      <Stack.Screen name="Login" component={Login} />
    </Stack.Navigator>
  );
};

const RootNavigation = () => {
  const authToken = useSelector((state) => state.authReducer.user.access_token);

  return (
    <NavigationContainer>
      {authToken ?
      <Stack.Navigator screenOptions={{ headerShown: false }}>
        <Stack.Screen name="Menu" component={Menu} />
       {/* <Stack.Screen name="Project" component={Project} /> */}
       <Stack.Screen name="ProfileSettings" component={ProfileSettings} />
      </Stack.Navigator> :
        <AuthStack />}
    </NavigationContainer>
  );
};

const App = () => {
  return (
    <Provider store={store}>
      <RootNavigation />
    </Provider>
  );
};

export default App;

</code>```
```<code>profileActionType.js

export const UDPATE_USER = "UDPATE_USER";
export const LOG_OUT = "LOG_OUT";
</code>```
```<code>logOutAction.js
import * as types from "../actions/profileActionType";
import { Alert } from "react-native"; // to show alerts in app

const url = new URL(
    "****" ( i hide my api since it is private )
);

const loggedOut = () => {
    return {
      type: types.LOG_OUT,
      // payload: logOutData.payload,

    };
  };

export const logOut = (headers) => {
    console.log("logged out",headers)
    return(dispatch) => {
        return fetch(url, {
            method: "GET",
            headers: headers,
        })
        .then(response => response.json())
        .then((json) => {
            if (json.status === "ok") {
              dispatch(loggedOut({ type: types.LOG_OUT, payload: json }));
              Alert.alert(json.message);
            } else {
              dispatch(loggedOut({ type: types.LOG_OUT, payload: {} }));
              Alert.alert(json.message);
            }
          })
          .catch((err) => {
            Alert.alert("Logout Failed", "Some error occured, please try again.");
            dispatch(loggedOut({ type: types.LOG_OUT, payload: {} }));
            console.log(err);
          });
    }
}
</code>```
```<code>logOutReducer.js

import * as types from "../actions/profileActionType";

const initialProfileState = {};

  const logOutReducer = (state = initialProfileState, action) => {
    switch (action.type) {
      case types.LOG_OUT:
        return {
          ...state,
          user: action.payload,
        };
      default:
        return state;
    }
  };

  export default logOutReducer;
</code>```
```<code>Profile.js

import React from 'react'
import {
  View,
  Text,
  SafeAreaView,
  TouchableOpacity,
  Image,
} from "react-native";
import {
  useFonts,
  NunitoSans_400Regular,
  NunitoSans_700Bold,
  NunitoSans_800ExtraBold,
} from "@expo-google-fonts/nunito-sans";
import { useSelector, useDispatch } from "react-redux";
import styles from "./ProfileStyleSheet";
import AppLoading from "expo-app-loading";
import {logOut} from '../../Redux/actions/logOutAction'

const Profile = ({navigation}) => {
  userInfo = useSelector((state) => state.authReducer.user)
  const dispatch = useDispatch();

  let [fontsLoaded] = useFonts({
    NunitoSans_400Regular,
    NunitoSans_700Bold,
    NunitoSans_800ExtraBold,
  });

  if (!fontsLoaded) {
    return <AppLoading />;
  }

  const token = userInfo.access_token
  console.log("logout token", token)

  const headers = {
    Authorization: "Bearer " + token,
    "Content-Type": "application/json",
    Accept: "application/json",
  };

  const handleLogOut = (e) => {
    e.preventDefault();
    dispatch(logOut(headers))
    navigation.navigate("Login")
  }

  const navigationProfileSettings = () => {
    navigation.navigate("ProfileSettings")
  }

  return (
    <SafeAreaView style={styles.container}>
      <Text style={styles.title}>Profile</Text>
      <View style={styles.infoContainer}>
        <View style={styles.logoContainer}>
          <Image
            style={styles.tinyLogo}
            source={{
              uri: `${userInfo.user.avatar}`,
            }}
          />
        </View>
        <View style={styles.titleContainer}>
          <Text style={styles.titleName}>{userInfo.user.name}</Text>
          <View style={{flexDirection: 'row'}}>
            <Text style={styles.description}>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Id facilisi cum
            </Text>
          </View>
        </View>
      </View>
      <View style={styles.linksLogoContainer}>
        <View>
          {/* <NotificationLogo style={styles.tinyLogo}/> */}
          <Text style={styles.description}>Notification</Text>
        </View>
        <View style={styles.line} />
        <View>
          <TouchableOpacity onPress={navigationProfileSettings}>
            <Text style={styles.description}>Profile Settings</Text>
        </TouchableOpacity>
        </View>
        <View style={styles.line} />
        <View>
          <TouchableOpacity onPress={handleLogOut}>
            <Text style={styles.description}>Log Out</Text>
        </TouchableOpacity>
        </View>
      </View>
    </SafeAreaView>
  )
}

export default Profile
</code>```

答:
for this problem, I have come up with a solution and it is to reset the state because I needed to clear my token. the project has a logic if the user is logged in or not by the token. and changes the navigation. so the code below in store.js will clear the state and will log us out and redirect us to the login page. we can clear the state by state=undefined.</p>
```<code>const appReducer = combineReducers({
  logOutReducer: logOutReducer,
});

const rootReducer = (state, action) => {
  if (action.type === LOG_OUT) {
    state = undefined;
  }
  return appReducer(state, action)
}

const store = createStore(
  rootReducer,
  composeEnhancers(applyMiddleware(thunk))
);

export default store;
</code>```

Body:
I am learning React Native and Redux and building a project.
I have a Profile Page where user can log out. I want to redirect the page to Login after logged out. but I receive below error.
"The action 'NAVIGATE' with payload {"name":"Login"} was not handled by any navigator.
Do you have a screen named 'Login'?"

Navigation is built by someone else in this project so I cant change the structure.
Basically in App.js there is structure checking if the user has the authToken or not. if the user does not have it, login page appears and it is outside of the RootNavigation. if the user has authToken, they can see the homepage which is inside of RootNavigation. The problem is that. Login component is outside of the RootNavigation so it doesnt catch it. How can i solve this problem and navigate to login without breaking this structure ?

Below are the codes.

App.js

import { StyleSheet, Text, View } from "react-native";
import store from "./src/Redux/store";
import Login from "./src/Components/Login/Login";
import Profile from "./src/Components/Profile/Profile";
import ProjectList from "./src/Components/ProjectList/ProjectList";
import { Provider, useSelector } from "react-redux";
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import Menu from "./src/Components/Menu/Menu";
import ProfileSettings from "./src/Components/ProfileSettings/ProfileSettings"

const Stack = createStackNavigator();
console.log(store.getState())

const AuthStack = () => {
  return (
    <Stack.Navigator screenOptions={{ headerShown: false }}>
      <Stack.Screen name="Login" component={Login} />
    </Stack.Navigator>
  );
};

const RootNavigation = () => {
  const authToken = useSelector((state) => state.authReducer.user.access_token);

  return (
    <NavigationContainer>
      {authToken ?
      <Stack.Navigator screenOptions={{ headerShown: false }}>
        <Stack.Screen name="Menu" component={Menu} />
       {/* <Stack.Screen name="Project" component={Project} /> */}
       <Stack.Screen name="ProfileSettings" component={ProfileSettings} />
      </Stack.Navigator> :
        <AuthStack />}
    </NavigationContainer>
  );
};

const App = () => {
  return (
    <Provider store={store}>
      <RootNavigation />
    </Provider>
  );
};

export default App;

profileActionType.js

export const UDPATE_USER = "UDPATE_USER";
export const LOG_OUT = "LOG_OUT";

logOutAction.js
import * as types from "../actions/profileActionType";
import { Alert } from "react-native"; // to show alerts in app

const url = new URL(
    "****" ( i hide my api since it is private )
);

const loggedOut = () => {
    return {
      type: types.LOG_OUT,
      // payload: logOutData.payload,

    };
  };

export const logOut = (headers) => {
    console.log("logged out",headers)
    return(dispatch) => {
        return fetch(url, {
            method: "GET",
            headers: headers,
        })
        .then(response => response.json())
        .then((json) => {
            if (json.status === "ok") {
              dispatch(loggedOut({ type: types.LOG_OUT, payload: json }));
              Alert.alert(json.message);
            } else {
              dispatch(loggedOut({ type: types.LOG_OUT, payload: {} }));
              Alert.alert(json.message);
            }
          })
          .catch((err) => {
            Alert.alert("Logout Failed", "Some error occured, please try again.");
            dispatch(loggedOut({ type: types.LOG_OUT, payload: {} }));
            console.log(err);
          });
    }
}

logOutReducer.js

import * as types from "../actions/profileActionType";

const initialProfileState = {};

  const logOutReducer = (state = initialProfileState, action) => {
    switch (action.type) {
      case types.LOG_OUT:
        return {
          ...state,
          user: action.payload,
        };
      default:
        return state;
    }
  };

  export default logOutReducer;

Profile.js

import React from 'react'
import {
  View,
  Text,
  SafeAreaView,
  TouchableOpacity,
  Image,
} from "react-native";
import {
  useFonts,
  NunitoSans_400Regular,
  NunitoSans_700Bold,
  NunitoSans_800ExtraBold,
} from "@expo-google-fonts/nunito-sans";
import { useSelector, useDispatch } from "react-redux";
import styles from "./ProfileStyleSheet";
import AppLoading from "expo-app-loading";
import {logOut} from '../../Redux/actions/logOutAction'

const Profile = ({navigation}) => {
  userInfo = useSelector((state) => state.authReducer.user)
  const dispatch = useDispatch();

  let [fontsLoaded] = useFonts({
    NunitoSans_400Regular,
    NunitoSans_700Bold,
    NunitoSans_800ExtraBold,
  });

  if (!fontsLoaded) {
    return <AppLoading />;
  }

  const token = userInfo.access_token
  console.log("logout token", token)

  const headers = {
    Authorization: "Bearer " + token,
    "Content-Type": "application/json",
    Accept: "application/json",
  };

  const handleLogOut = (e) => {
    e.preventDefault();
    dispatch(logOut(headers))
    navigation.navigate("Login")
  }

  const navigationProfileSettings = () => {
    navigation.navigate("ProfileSettings")
  }

  return (
    <SafeAreaView style={styles.container}>
      <Text style={styles.title}>Profile</Text>
      <View style={styles.infoContainer}>
        <View style={styles.logoContainer}>
          <Image
            style={styles.tinyLogo}
            source={{
              uri: `${userInfo.user.avatar}`,
            }}
          />
        </View>
        <View style={styles.titleContainer}>
          <Text style={styles.titleName}>{userInfo.user.name}</Text>
          <View style={{flexDirection: 'row'}}>
            <Text style={styles.description}>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit. Id facilisi cum
            </Text>
          </View>
        </View>
      </View>
      <View style={styles.linksLogoContainer}>
        <View>
          {/* <NotificationLogo style={styles.tinyLogo}/> */}
          <Text style={styles.description}>Notification</Text>
        </View>
        <View style={styles.line} />
        <View>
          <TouchableOpacity onPress={navigationProfileSettings}>
            <Text style={styles.description}>Profile Settings</Text>
        </TouchableOpacity>
        </View>
        <View style={styles.line} />
        <View>
          <TouchableOpacity onPress={handleLogOut}>
            <Text style={styles.description}>Log Out</Text>
        </TouchableOpacity>
        </View>
      </View>
    </SafeAreaView>
  )
}

export default Profile

Body:
for this problem, I have come up with a solution and it is to reset the state because I needed to clear my token. the project has a logic if the user is logged in or not by the token. and changes the navigation. so the code below in store.js will clear the state and will log us out and redirect us to the login page. we can clear the state by state=undefined.

const appReducer = combineReducers({
  logOutReducer: logOutReducer,
});

const rootReducer = (state, action) => {
  if (action.type === LOG_OUT) {
    state = undefined;
  }
  return appReducer(state, action)
}

const store = createStore(
  rootReducer,
  composeEnhancers(applyMiddleware(thunk))
);

export default store;


来源: stackexchange

问:
How to push response of an post API into an array using pipe and map operator in angular . It shows [object Object]
I am working on a project where in my component.ts file I am calling a post API which is giving me a response in the format

```<code> email: string,
  employeeAddress: string,
  employeeId: string,
employeeLevel: string
</code>```

I am not sure whether my approach is correct or not but here is
my API calling code

```<code> return this.http.post<{[employeeId : string] : Create}>('http://localhost:9900/api/v1/employee',postData).
    pipe(map(response => {
      var res = JSON.parse(JSON.stringify(response))
        const getCreateempIdArray : any = [] ;

       console.log(res)
        for(const employeeId in res){
          if(res.hasOwnProperty(employeeId)){
            getCreateempIdArray.push({ ...res[employeeId]});
            }
           if(res.hasOwnProperty(email)){
             getCreateempIdArray .push({...res[email]});
           }
        }
        console.log("Employee Id Array from Create employee Service " + getCreateempIdArray)
        return getCreateempIdArray;
      })).

     subscribe((responseData)=>
    {
       console.log(response)

      })
</code>```

Now the thing I want is that when I get my response then I want to push my employeeId and email corresponding to that employeeId in an array so that I can save the array in local storage and I can get the particular employee email through employeeId by the help of this array.

But when I do console.log(getCreateempIdArray) it gives me [object Object]

I just want to push the employeeId and its corresponding emailId which I am getting from response body into an array

答:
It is shown in the form [object Object] because you are concatenating a string with an object (using ' ' +) and automatically the object is converted to a string.

try this way:

```<code>console.log("Employee Id Array from Create employee Service ", getCreateempIdArray)
</code>```

Instead of using a (+), use a comma (,) to separate the string and the object.

ContentLicense: CC BY-SA 4.0

Body:
I am working on a project where in my component.ts file I am calling a post API which is giving me a response in the format

 email: string,
  employeeAddress: string,
  employeeId: string,
employeeLevel: string

I am not sure whether my approach is correct or not but here is
my API calling code

 return this.http.post<{[employeeId : string] : Create}>('http://localhost:9900/api/v1/employee',postData).
    pipe(map(response => {
      var res = JSON.parse(JSON.stringify(response))
        const getCreateempIdArray : any = [] ;

       console.log(res)
        for(const employeeId in res){
          if(res.hasOwnProperty(employeeId)){
            getCreateempIdArray.push({ ...res[employeeId]});
            }
           if(res.hasOwnProperty(email)){
             getCreateempIdArray .push({...res[email]});
           }
        }
        console.log("Employee Id Array from Create employee Service " + getCreateempIdArray)
        return getCreateempIdArray;
      })).

     subscribe((responseData)=>
    {
       console.log(response)

      })

Now the thing I want is that when I get my response then I want to push my employeeId and email corresponding to that employeeId in an array so that I can save the array in local storage and I can get the particular employee email through employeeId by the help of this array.

But when I do console.log(getCreateempIdArray) it gives me [object Object]

I just want to push the employeeId and its corresponding emailId which I am getting from response body into an array

Body:
It is shown in the form [object Object] because you are concatenating a string with an object (using ' ' +) and automatically the object is converted to a string.

try this way:

console.log("Employee Id Array from Create employee Service ", getCreateempIdArray)

Instead of using a (+), use a comma (,) to separate the string and the object.


问:
Github actions workflows are canceled right away
I have a problem with running github actions on branches created automatically from within other action. Config file triggers them on push to branches that start with feature-.

Then I have a python script using PyGithub and create_git_ref for branch creation. Everything works fine if I do it from local terminal, branch is created and actions are triggered properly, things change when the same script is launched from within github action using GITHUB_TOKEN as an authorization, then those actions are canceled even before they start, anyone experienced such a problem before? What am I missing?

答:
The GITHUB_TOKEN has a specific scope os permissions, and it doesn't have the one(s) you need here.

Instead, you need to use a Personal Access Token and add it as a secret (for example ACCESS_TOKEN as you can't use the GITHUB_ prefix) and call it in your workflow using ${{ secrets.ACCESS_TOKEN }}.


问:
I am trying to add files to google drive using kotlin in cororutines but getting error that main thread will be blocked even if i am using coroutine
Here is the issue
I am making the file and then adding to google drive but it says inappropriate blocking method call.

答:
You should run long running task in another Thread or use Coroutine to execute blocking method.

by using Coroutine it will be sth like this:

```<code>    withContext(Dispatchers.IO) {
        launch{
          mfile.execute();
        }
    }

</code>```

Body:
You should run long running task in another Thread or use Coroutine to execute blocking method.

by using Coroutine it will be sth like this:

    withContext(Dispatchers.IO) {
        launch{
          mfile.execute();
        }
    }


问:
`docker-compose up` doesn't create a persistent volume for PostgreSQL container
I am trying to create a persistent volume storage for a postgres docker container. Because I will have some other services within this docker environment, I am using docker-compose to start the container.

The contents of my docker-compose.yml file are:

```<code>version: "3.8"

services:
  db:
    image: postgres:12.3
    volumes:
      - ./postgres-data:/var/lib/posgresql/data
    environment:
      - POSTGRES_NAME=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres

volumes:
  postgres-data:
</code>```

I run the command:

```<code>docker-compose up
</code>```

Now, based on the documentation, and this example, I expect to have one persistent volume created, with its name including postgres-data (e.g., postgres-volume_postgres-data).

Instead, two volumes are created:

```<code>docker volume ls
</code>```

Output:

```<code>DRIVER    VOLUME NAME
local     a53a5b161f0fefca840e2f0cd2a97ad019864dea23c7f23f07c94e6be7ace601
local     postgres-volume_postgres-data
</code>```

Inspecting the volumes:

```<code>docker volume inspect a53a5b161f0fefca840e2f0cd2a97ad019864dea23c7f23f07c94e6be7ace601
</code>```

Output:

```<code>[
    {
        "CreatedAt": "2022-02-07T19:12:04Z",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/a53a5b161f0fefca840e2f0cd2a97ad019864dea23c7f23f07c94e6be7ace601/_data",
        "Name": "a53a5b161f0fefca840e2f0cd2a97ad019864dea23c7f23f07c94e6be7ace601",
        "Options": null,
        "Scope": "local"
    }
]
</code>```

Inspecting the second volume:

```<code>docker volume inspect postgres-volume_postgres-data
</code>```

Output:

```<code>[
    {
        "CreatedAt": "2022-02-07T19:12:02Z",
        "Driver": "local",
        "Labels": {
            "com.docker.compose.project": "postgres-volume",
            "com.docker.compose.version": "1.29.2",
            "com.docker.compose.volume": "postgres-data"
        },
        "Mountpoint": "/var/lib/docker/volumes/postgres-volume_postgres-data/_data",
        "Name": "postgres-volume_postgres-data",
        "Options": null,
        "Scope": "local"
    }
]
</code>```

Of these two volumes only the a53a5b161f0fefca840e2f0cd2a97ad019864dea23c7f23f07c94e6be7ace601 is active and all the data seem to be stored within this volume.

The effect is that when I use docker-compose down, all data saved to the database when the container was running are lost, i.e., the data are not persisted, and they are not available after running the docker-compose up again.

What I am doing wrong?

(I am using macOS Big Sur, v.11.4.)

Docker & docker-compose versions:

```<code>➜  ~ docker --version
Docker version 20.10.7, build f0df350

➜  ~ docker-compose version
docker-compose version 1.29.2, build 5becea4c
docker-py version: 5.0.0
CPython version: 3.9.0
OpenSSL version: OpenSSL 1.1.1h  22 Sep 2020
</code>```

答:
Currently, you are mounting a host path. Its contents are lost when removing containers. What you want to have is a named volume. After the initial creation of a named volume, you can reuse it across services and it will be persisted on following invocations.

In docker-compose, the following would create the named volume myapp and use it in the service called 'frontend'. The docs and compose reference explain this well.

version: "3.9"
services:
  frontend:
    image: node:lts
    volumes:
      - myapp:/home/node/app
volumes:
  myapp:
```
<p>Actually, you also can create volumes externally with <code>docker volume create myapp</code> and then reference them inside <code>docker-compose</code> setups adding <code>external: true</code>.</p>
<pre class="lang-yaml prettyprint-override"><code>volumes:
  myapp:
    external: true
</code>```

Body:
I am trying to create a persistent volume storage for a postgres docker container. Because I will have some other services within this docker environment, I am using docker-compose to start the container.

The contents of my docker-compose.yml file are:

version: "3.8"

services:
  db:
    image: postgres:12.3
    volumes:
      - ./postgres-data:/var/lib/posgresql/data
    environment:
      - POSTGRES_NAME=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres

volumes:
  postgres-data:

I run the command:

docker-compose up

Now, based on the documentation, and this example, I expect to have one persistent volume created, with its name including postgres-data (e.g., postgres-volume_postgres-data).

Instead, two volumes are created:

docker volume ls

Output:

DRIVER    VOLUME NAME
local     a53a5b161f0fefca840e2f0cd2a97ad019864dea23c7f23f07c94e6be7ace601
local     postgres-volume_postgres-data

Inspecting the volumes:

docker volume inspect a53a5b161f0fefca840e2f0cd2a97ad019864dea23c7f23f07c94e6be7ace601

Output:

[
    {
        "CreatedAt": "2022-02-07T19:12:04Z",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/a53a5b161f0fefca840e2f0cd2a97ad019864dea23c7f23f07c94e6be7ace601/_data",
        "Name": "a53a5b161f0fefca840e2f0cd2a97ad019864dea23c7f23f07c94e6be7ace601",
        "Options": null,
        "Scope": "local"
    }
]

Inspecting the second volume:

docker volume inspect postgres-volume_postgres-data

Output:

[
    {
        "CreatedAt": "2022-02-07T19:12:02Z",
        "Driver": "local",
        "Labels": {
            "com.docker.compose.project": "postgres-volume",
            "com.docker.compose.version": "1.29.2",
            "com.docker.compose.volume": "postgres-data"
        },
        "Mountpoint": "/var/lib/docker/volumes/postgres-volume_postgres-data/_data",
        "Name": "postgres-volume_postgres-data",
        "Options": null,
        "Scope": "local"
    }
]

Of these two volumes only the a53a5b161f0fefca840e2f0cd2a97ad019864dea23c7f23f07c94e6be7ace601 is active and all the data seem to be stored within this volume.

The effect is that when I use docker-compose down, all data saved to the database when the container was running are lost, i.e., the data are not persisted, and they are not available after running the docker-compose up again.

What I am doing wrong?

(I am using macOS Big Sur, v.11.4.)

Docker & docker-compose versions:

➜  ~ docker --version
Docker version 20.10.7, build f0df350

➜  ~ docker-compose version
docker-compose version 1.29.2, build 5becea4c
docker-py version: 5.0.0
CPython version: 3.9.0
OpenSSL version: OpenSSL 1.1.1h  22 Sep 2020

Body:
Currently, you are mounting a host path. Its contents are lost when removing containers. What you want to have is a named volume. After the initial creation of a named volume, you can reuse it across services and it will be persisted on following invocations.

In docker-compose, the following would create the named volume myapp and use it in the service called 'frontend'. The docs and compose reference explain this well.

version: "3.9"
services:
  frontend:
    image: node:lts
    volumes:
      - myapp:/home/node/app
volumes:
  myapp:

Actually, you also can create volumes externally with docker volume create myapp and then reference them inside docker-compose setups adding external: true.

volumes:
  myapp:
    external: true


问:
how to stream microphone audio from browser to S3
I want to stream the microphone audio from the web browser to AWS S3.
Got it working

```<code>this.recorder = new window.MediaRecorder(...);
this.recorder.addEventListener('dataavailable', (e) => {
  this.chunks.push(e.data);
});
</code>```

and then when user clicks on stop upload the chunks new Blob(this.chunks, { type: 'audio/wav' }) as multiparts to AWS S3.

But the problem is if the recording is 2-3 hours longer then it might take exceptionally longer and user might close the browser before waiting for the recording to complete uploading.

Is there a way we can stream the web audio directly to S3 while it's going on?

Things I tried but can't get a working example:

Kineses video streams, looks like it's only for real time streaming between multiple clients and I have to write my own client which will then save it to S3.

Thought to use kinesis data firehose but couldn't find any client data producer from brower.

Even tried to find any resource using aws lex or aws ivs but I think they are just over engineering for my use case.

Any help will be appreciated.

答:
You can set the timeslice parameter when calling start() on the MediaRecorder. The MediaRecorder will then emit chunks which roughly match the length of the timeslice parameter.

You could upload those chunks using S3's multipart upload feature as you already mentioned.

Please note that you need a library like extendable-media-recorder if you want to record a WAV file since no browser supports that out of the box.

Body:
I want to stream the microphone audio from the web browser to AWS S3.
Got it working

this.recorder = new window.MediaRecorder(...);
this.recorder.addEventListener('dataavailable', (e) => {
  this.chunks.push(e.data);
});

and then when user clicks on stop upload the chunks new Blob(this.chunks, { type: 'audio/wav' }) as multiparts to AWS S3.

But the problem is if the recording is 2-3 hours longer then it might take exceptionally longer and user might close the browser before waiting for the recording to complete uploading.

Is there a way we can stream the web audio directly to S3 while it's going on?

Things I tried but can't get a working example:

Kineses video streams, looks like it's only for real time streaming between multiple clients and I have to write my own client which will then save it to S3.

Thought to use kinesis data firehose but couldn't find any client data producer from brower.

Even tried to find any resource using aws lex or aws ivs but I think they are just over engineering for my use case.

Any help will be appreciated.


问:
Unable to locate element within an iframe while the element actually exists
No matter how long I wait, it seems selenium can't find the "watch_online" button.

I've tried both by XPath, full XPath, and CSS selector.

I want to get the href link from the "Watch Online" button.

```<code>import os
import glob
import time

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.ui import WebDriverWait

# Create local browser cache
browser_data_location = "browser_data"
options = webdriver.ChromeOptions()
options.add_argument(f'user-data-dir={os.getcwd()}/{browser_data_location}')

i_m_not_a_robot_xpath = '//*[@id="landing"]/div[2]/center/img'
generate_link_xpath = '//*[@id="generater"]/img'
click_to_continue = '//*[@id="showlink"]'
get_download_link = '/html/body/section/div/div/div/center/a'
watch_online = '//*[@id="download-hidden"]/a'

with webdriver.Chrome(options=options, ) as driver:
    wait = WebDriverWait(driver, 10)

    time.sleep(2)

    driver.get(
        "https://www.rtilinks.com/?82255aba71=RmwzVDZObDFBdDQvay8zRjhiaStoM004Ymd1T201MnBQelJpdW5oK1UxeGFvbFZUY1FEVXMrY0o2UnhqeGxOOFlwN3JlUElad2h0ek9pQ1ZFZndXSG9UTzA1aFpmTEhoanBVUldEYWwwWVU9")

    # wait.until(ec.element_to_be_clickable((By.CSS_SELECTOR, upload_box_css))).send_keys(file)

    wait.until(ec.element_to_be_clickable((By.XPATH, i_m_not_a_robot_xpath))).click()
    # time.sleep(1)
    wait.until(ec.element_to_be_clickable((By.XPATH, generate_link_xpath))).click()
    wait.until(ec.element_to_be_clickable((By.XPATH, click_to_continue))).click()
    # original_window = driver.current_window_handle

    driver.close()
    driver.switch_to.window(driver.window_handles[0])

    wait.until(ec.element_to_be_clickable((By.XPATH, get_download_link))).click()

    time.sleep(2)
    link = driver.find_element(By.XPATH, watch_online)
    print(link.get_attribute('href'))
</code>```

答:
The element Watch Online is within an  so you have to:

Induce WebDriverWait for the desired frame to be available and switch to it.

Induce WebDriverWait for the desired element to be clickable.

You can use either of the following Locator Strategies:

Using PARTIAL_LINK_TEXT:

```<code>WebDriverWait(driver, 20).until(EC.frame_to_be_available_and_switch_to_it((By.XPATH,"//iframe[starts-with(@src, 'https://purefiles.in')]")))
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.PARTIAL_LINK_TEXT, "Watch Online"))).click()
</code>```

Using CSS_SELECTOR:

```<code>WebDriverWait(driver, 20).until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR,"iframe[src^='https://purefiles.in']")))
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "a.button.is-success"))).click()
</code>```

Using XPATH:

```<code>WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//a[@class='button is-success' and contains(., 'Watch Online')]"))).click()
WebDriverWait(driver, 20).until(EC.frame_to_be_available_and_switch_to_it((By.XPATH,"//iframe[starts-with(@src, 'https://purefiles.in')]")))
</code>```

Note : You have to add the following imports :

```<code> from selenium.webdriver.support.ui import WebDriverWait
 from selenium.webdriver.common.by import By
 from selenium.webdriver.support import expected_conditions as EC
</code>```

Reference

You can find a couple of relevant discussions in:

Switch to an iframe through Selenium and python

selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element while trying to click Next button with selenium

selenium in python : NoSuchElementException: Message: no such element: Unable to locate element

Body:
No matter how long I wait, it seems selenium can't find the "watch_online" button.

I've tried both by XPath, full XPath, and CSS selector.

I want to get the href link from the "Watch Online" button.

import os
import glob
import time

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.ui import WebDriverWait

# Create local browser cache
browser_data_location = "browser_data"
options = webdriver.ChromeOptions()
options.add_argument(f'user-data-dir={os.getcwd()}/{browser_data_location}')

i_m_not_a_robot_xpath = '//*[@id="landing"]/div[2]/center/img'
generate_link_xpath = '//*[@id="generater"]/img'
click_to_continue = '//*[@id="showlink"]'
get_download_link = '/html/body/section/div/div/div/center/a'
watch_online = '//*[@id="download-hidden"]/a'

with webdriver.Chrome(options=options, ) as driver:
    wait = WebDriverWait(driver, 10)

    time.sleep(2)

    driver.get(
        "https://www.rtilinks.com/?82255aba71=RmwzVDZObDFBdDQvay8zRjhiaStoM004Ymd1T201MnBQelJpdW5oK1UxeGFvbFZUY1FEVXMrY0o2UnhqeGxOOFlwN3JlUElad2h0ek9pQ1ZFZndXSG9UTzA1aFpmTEhoanBVUldEYWwwWVU9")

    # wait.until(ec.element_to_be_clickable((By.CSS_SELECTOR, upload_box_css))).send_keys(file)

    wait.until(ec.element_to_be_clickable((By.XPATH, i_m_not_a_robot_xpath))).click()
    # time.sleep(1)
    wait.until(ec.element_to_be_clickable((By.XPATH, generate_link_xpath))).click()
    wait.until(ec.element_to_be_clickable((By.XPATH, click_to_continue))).click()
    # original_window = driver.current_window_handle

    driver.close()
    driver.switch_to.window(driver.window_handles[0])

    wait.until(ec.element_to_be_clickable((By.XPATH, get_download_link))).click()

    time.sleep(2)
    link = driver.find_element(By.XPATH, watch_online)
    print(link.get_attribute('href'))

Body:
The element Watch Online is within an <iframe> so you have to:

Induce WebDriverWait for the desired frame to be available and switch to it.

Induce WebDriverWait for the desired element to be clickable.

You can use either of the following Locator Strategies:

Using PARTIAL_LINK_TEXT:

WebDriverWait(driver, 20).until(EC.frame_to_be_available_and_switch_to_it((By.XPATH,"//iframe[starts-with(@src, 'https://purefiles.in')]")))
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.PARTIAL_LINK_TEXT, "Watch Online"))).click()

Using CSS_SELECTOR:

WebDriverWait(driver, 20).until(EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR,"iframe[src^='https://purefiles.in']")))
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "a.button.is-success"))).click()

Using XPATH:

WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//a[@class='button is-success' and contains(., 'Watch Online')]"))).click()
WebDriverWait(driver, 20).until(EC.frame_to_be_available_and_switch_to_it((By.XPATH,"//iframe[starts-with(@src, 'https://purefiles.in')]")))

Note : You have to add the following imports :

 from selenium.webdriver.support.ui import WebDriverWait
 from selenium.webdriver.common.by import By
 from selenium.webdriver.support import expected_conditions as EC

Reference

You can find a couple of relevant discussions in:

Switch to an iframe through Selenium and python

selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element while trying to click Next button with selenium

selenium in python : NoSuchElementException: Message: no such element: Unable to locate element


问:
How to return Thymeleaf page with sent Date param
I need directions how to solve issue with CRUD app. I have model  class that have Date field:

```<code>@Column(name = "date", nullable = false)
@DateTimeFormat(pattern = "dd.MM.yyyy.")
private Date date;
</code>```

In Thymeleaf template date field is put in textfield that using JQuery datepicker. That part working as intended.

```<code><form th:action="@{/poetracker/addNew}" th:object="${item}" method="post" style="max-width:418px; margin:0 auto">
    <div class="pt-3">
        <div class="form-group row">
            <label for="datepicker" class="col-sm-4 col-form-label">Датум:</label>
            <div class="col-sm-8">
                <input type="text" id="datepicker" th:field="*{date}" readonly autocomplete="off" class="form-control form-control-sm"/>
            </div>
            //some other components rendering
        </div>
</form>
</code>```

Now comes the part I have trouble with. When submit button is clicked, item added to database, I want form to be reloaded. But, I want date field to be remembered from last item added to database (value that was in form before Submit button clicked).

here is my Controller, what I tried:

```<code>@GetMapping("/addNew")
public String addNewShow(@RequestParam(name = "date", required = false)
    @DateTimeFormat(iso = ISO.DATE) Date date, Model model) {
    Item item = new Item();
    model.addAttribute("item", item);
    item.setDate(date);
    return "/poetracker/addNew";
}

@PostMapping("/addNew")
public String addNew(Item item) {
    itemService.addNew(item);
    return "redirect:/poetracker/addNew";
}
</code>```

I tested passing date through query it works, but I want to be passed just with button click. Is there recommended way to do this? Java or JQuery, doesn't matter, just curious how it is done.

UPDATE: I am looking for this kind of functionality:
In .net razor-pages I have properties

```<code>[BindProperty(Name = "month", SupportsGet = true)]
public int? SelectedMonth { get; set; }
[BindProperty(Name = "year", SupportsGet = true)]
public int? SelectedYear { get; set; }
[BindProperty(Name = "day", SupportsGet = true)]
public int? SelectedDay { get; set; }
</code>```

and can redirect in Post like this:

```<code>public async Task<IActionResult> OnPostAsync()
    {
        if (ModelState.IsValid)
        {
            await repository.CreateAsync(Item!);
        }
        return RedirectToPage("Add", new
        {
            day = Item!.Day, month = Item!.Month, year = Item!.Year
        });
    }
</code>```

Is there something like that in Java? Probably there is, I just need pointers how.

答:
Finally managed to solve it. Not the only way and not exactly what I had in mind, but it provides desired results. Posting it, just in case someone need solution to similar problem:</p>
```<code>@PostMapping("/addNew")
public String addNew(Item item) {
    itemService.addNew(item);
    LocalDate date = item.getDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    String selectedDate = String.format("%d-%d-%d", date.getYear(), date.getMonthValue(), date.getDayOfMonth());
    String route = "redirect:/poetracker/addNew?date=" + selectedDate;
    return route;
}
</code>```

Body:
I need directions how to solve issue with CRUD app. I have model  class that have Date field:

@Column(name = "date", nullable = false)
@DateTimeFormat(pattern = "dd.MM.yyyy.")
private Date date;

In Thymeleaf template date field is put in textfield that using JQuery datepicker. That part working as intended.

<form th:action="@{/poetracker/addNew}" th:object="${item}" method="post" style="max-width:418px; margin:0 auto">

            Датум:

                <input type="text" id="datepicker" th:field="*{date}" readonly autocomplete="off" class="form-control form-control-sm"/>

            //some other components rendering

</form>

Now comes the part I have trouble with. When submit button is clicked, item added to database, I want form to be reloaded. But, I want date field to be remembered from last item added to database (value that was in form before Submit button clicked).

here is my Controller, what I tried:

@GetMapping("/addNew")
public String addNewShow(@RequestParam(name = "date", required = false)
    @DateTimeFormat(iso = ISO.DATE) Date date, Model model) {
    Item item = new Item();
    model.addAttribute("item", item);
    item.setDate(date);
    return "/poetracker/addNew";
}

@PostMapping("/addNew")
public String addNew(Item item) {
    itemService.addNew(item);
    return "redirect:/poetracker/addNew";
}

I tested passing date through query it works, but I want to be passed just with button click. Is there recommended way to do this? Java or JQuery, doesn't matter, just curious how it is done.

UPDATE: I am looking for this kind of functionality:
In .net razor-pages I have properties

[BindProperty(Name = "month", SupportsGet = true)]
public int? SelectedMonth { get; set; }
[BindProperty(Name = "year", SupportsGet = true)]
public int? SelectedYear { get; set; }
[BindProperty(Name = "day", SupportsGet = true)]
public int? SelectedDay { get; set; }

and can redirect in Post like this:

public async Task<IActionResult> OnPostAsync()
    {
        if (ModelState.IsValid)
        {
            await repository.CreateAsync(Item!);
        }
        return RedirectToPage("Add", new
        {
            day = Item!.Day, month = Item!.Month, year = Item!.Year
        });
    }

Is there something like that in Java? Probably there is, I just need pointers how.

Body:
Finally managed to solve it. Not the only way and not exactly what I had in mind, but it provides desired results. Posting it, just in case someone need solution to similar problem:

@PostMapping("/addNew")
public String addNew(Item item) {
    itemService.addNew(item);
    LocalDate date = item.getDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    String selectedDate = String.format("%d-%d-%d", date.getYear(), date.getMonthValue(), date.getDayOfMonth());
    String route = "redirect:/poetracker/addNew?date=" + selectedDate;
    return route;
}


问:
Is it possible to dynamically reference a list from a file?
I'm currently working on a python bot that references variables from different users based on the input from the user. The user enters in the name of a file in the folder, which has a list saved to it. All of the files have the same list name, but each list has different elements saved. The idea is that the files act as accounts for the user, and each one has its own keys saved in the list.

I want to then set a variable to the contents of the list from whatever file the user selected, like so:

```<code>import file1
import file2
import file3

filePath = input()
fileContents = filePath.list
</code>```

This displays an error because the filePath variable is technically a string and does not have a list attribute.

My question is:

How can I set fileContents to the contents of the list that the user chosen file contains?

答:
For a single file, you can get the lines in the file with .splitlines() or iterating over it

with open("input.txt") as fh:
    lines = fh.read().splitlines()
```
<pre class="lang-py prettyprint-override"><code>with open("input.txt") as fh:
    for line in fh:
        print(line), end="")
</code>```

Body:
I'm currently working on a python bot that references variables from different users based on the input from the user. The user enters in the name of a file in the folder, which has a list saved to it. All of the files have the same list name, but each list has different elements saved. The idea is that the files act as accounts for the user, and each one has its own keys saved in the list.

I want to then set a variable to the contents of the list from whatever file the user selected, like so:

import file1
import file2
import file3

filePath = input()
fileContents = filePath.list

This displays an error because the filePath variable is technically a string and does not have a list attribute.

My question is:

How can I set fileContents to the contents of the list that the user chosen file contains?

Body:
For a single file, you can get the lines in the file with .splitlines() or iterating over it

with open("input.txt") as fh:
    lines = fh.read().splitlines()

with open("input.txt") as fh:
    for line in fh:
        print(line), end="")


问:
Remove label from line chart - react-chartjs-2
I am looking to remove the labels that are in the square in the following picture: https://prnt.sc/26qc8uq. How would I do it? I tried with options: {plugins: {legend: display: false}} but it didn't seem to work. Package I am using is react-chartjs-2

This is the code:

```<code><Chart type="line" data={{
  labels: props.sets[0],
  datasets: props.dataset,
  options: {
   plugins: {
     legend: {
       display: false,
     },
   }
  }
}} />

</code>```

答:
You are passing the options to the data prop, this is incorrect. You will need to pass the options to the options prop like so:

<Chart type="line"
  options={{
    plugins: {
      legend: {
        display: false,
      },
    }
  }}
  data={{
    labels: props.sets[0],
    datasets: props.dataset,
  }}
/>
```

Body:
I am looking to remove the labels that are in the square in the following picture: https://prnt.sc/26qc8uq. How would I do it? I tried with options: {plugins: {legend: display: false}} but it didn't seem to work. Package I am using is react-chartjs-2

This is the code:

<Chart type="line" data={{
  labels: props.sets[0],
  datasets: props.dataset,
  options: {
   plugins: {
     legend: {
       display: false,
     },
   }
  }
}} />


问:
Saving data in useState variable with select form in React
I have an array of objects which contain properties of their size and quantity (FE: {size:"Large", quantity: 10}). I want the user to be able to select any available size in the form and then store it in useState. To be honset I do something like that for the first time and sadly my code is not working. I get basillion errors starting with "event is undeffined". I'm pasting my code below:</p>
```<code>    const handleChange = (event) => {
        setChosenSize(event.target.value)
    }

    const choseSize = (array) => {
        return (<>
             <form>
                <label>Choos your size:</label>
                <select onChange={() => handleChange()}>
                    {array.map((object) => {
                    return (
                        <option value={object.size}>{object.size}: {object.quantity}</option>
                    )
                    })}
                </select>
            </form>
        </>)
    }
    console.log(chosenSize)
</code>```

答:
You are running the function without passing anything inside it so event will either be undefined or null. The following should fix your problem:

```<code>const handleChange = (event) => {
        setChosenSize(event.target.value)
    }

    const choseSize = (array) => {
        return (<>
             <form>
                <label>Choos your size:</label>
                <select onChange={(event) => handleChange(event)}>
                    {array.map((object) => {
                    return (
                        <option value={object.size}>{object.size}: {object.quantity}</option>
                    )
                    })}
                </select>
            </form>
        </>)
    }
    console.log(chosenSize)

</code>```

or you can do the following (which I prefer)

```<code>const handleChange = (event) => {
        setChosenSize(event.target.value)
    }

    const choseSize = (array) => {
        return (<>
             <form>
                <label>Choos your size:</label>
                <select onChange={handleChange}>
                    {array.map((object) => {
                    return (
                        <option value={object.size}>{object.size}: {object.quantity}</option>
                    )
                    })}
                </select>
            </form>
        </>)
    }
    console.log(chosenSize)
</code>```

Body:
I have an array of objects which contain properties of their size and quantity (FE: {size:"Large", quantity: 10}). I want the user to be able to select any available size in the form and then store it in useState. To be honset I do something like that for the first time and sadly my code is not working. I get basillion errors starting with "event is undeffined". I'm pasting my code below:

    const handleChange = (event) => {
        setChosenSize(event.target.value)
    }

    const choseSize = (array) => {
        return (<>
             <form>
                Choos your size:
                <select onChange={() => handleChange()}>
                    {array.map((object) => {
                    return (
                        <option value={object.size}>{object.size}: {object.quantity}</option>
                    )
                    })}
                </select>
            </form>
        </>)
    }
    console.log(chosenSize)

Body:
You are running the function without passing anything inside it so event will either be undefined or null. The following should fix your problem:

const handleChange = (event) => {
        setChosenSize(event.target.value)
    }

    const choseSize = (array) => {
        return (<>
             <form>
                <label>Choos your size:</label>
                <select onChange={(event) => handleChange(event)}>
                    {array.map((object) => {
                    return (
                        <option value={object.size}>{object.size}: {object.quantity}</option>
                    )
                    })}
                </select>
            </form>
        </>)
    }
    console.log(chosenSize)

or you can do the following (which I prefer)

const handleChange = (event) => {
        setChosenSize(event.target.value)
    }

    const choseSize = (array) => {
        return (<>
             <form>
                <label>Choos your size:</label>
                <select onChange={handleChange}>
                    {array.map((object) => {
                    return (
                        <option value={object.size}>{object.size}: {object.quantity}</option>
                    )
                    })}
                </select>
            </form>
        </>)
    }
    console.log(chosenSize)


问:
Dealing with PHP 8.1 warning for undefined array key
We are upgrading to PHP 8.1. A new feature is that an undefined array key throws a warning.

Unfortunately this interferes with the ability to easily use associative arrays such as $_SESSION variables. I understand the virtues of predefining variables, and am not looking for a discussion on those virtues. The idea of the associative array is that you can add things easily to the session and everything not so assigned is evaluated as null. But now it also throws a warning; something has to be done to deal with that.

Here is an example of some code:

```<code>$_SESSION['is_condition'] = true;
</code>```

In another place in the code, the following occurs

```<code>if ($_SESSION['is_condition']) ...
</code>```

If this occurs in a context where the 'is_condition' session variable has not been defined, the desired result of evaluating its value as null is what we want. But now something else has to be done to deal with the possibility that it is undefined.

There are several approaches to dealing with this:

Pre-define all session variables having the value of null. Seems like it is not the spirit of associative arrays. Now every script has to invoke a lengthy set of code.

Use the null coalesce operator whenever the value of an associative array element is required. This is an ugly requirement to place many, many additional operators throughout the code base.

Alter our custom error handling functions to ignore the undefined array key error. A very bad idea to suppress warnings, and adds overhead.

None of these approaches is very desirable.

A theoretical way to solve this would be an array initialization statement that assigns all possible associative keys to null. I don't know of any such thing.

My question is whether there is some other approach that I am missing, such as a parameter that suppresses this specific warning only.

答:
You are assuming that $_SESSION['whatever'] is always going to be available. You can wrap all your conditionals with isset like so:

```<code>if (isset($_SESSION['is_condition'])) {
//Do Whatever
}
</code>```

Or you can do something unweildy like putting this at the top of your file.

```<code>if (isset($_SESSION['is_condition'])) {
//Do something
} else {
$_SESSION['is_condition'] = NULL;
}
</code>```

From then on anytime you check the session for that key it will at least be set to something and wont throw a warning or notice. It's ugly but it will work.

Body:
We are upgrading to PHP 8.1. A new feature is that an undefined array key throws a warning.

Unfortunately this interferes with the ability to easily use associative arrays such as $_SESSION variables. I understand the virtues of predefining variables, and am not looking for a discussion on those virtues. The idea of the associative array is that you can add things easily to the session and everything not so assigned is evaluated as null. But now it also throws a warning; something has to be done to deal with that.

Here is an example of some code:

$_SESSION['is_condition'] = true;

In another place in the code, the following occurs

if ($_SESSION['is_condition']) ...

If this occurs in a context where the 'is_condition' session variable has not been defined, the desired result of evaluating its value as null is what we want. But now something else has to be done to deal with the possibility that it is undefined.

There are several approaches to dealing with this:

Pre-define all session variables having the value of null. Seems like it is not the spirit of associative arrays. Now every script has to invoke a lengthy set of code.

Use the null coalesce operator whenever the value of an associative array element is required. This is an ugly requirement to place many, many additional operators throughout the code base.

Alter our custom error handling functions to ignore the undefined array key error. A very bad idea to suppress warnings, and adds overhead.

None of these approaches is very desirable.

A theoretical way to solve this would be an array initialization statement that assigns all possible associative keys to null. I don't know of any such thing.

My question is whether there is some other approach that I am missing, such as a parameter that suppresses this specific warning only.

Body:
You are assuming that $_SESSION['whatever'] is always going to be available. You can wrap all your conditionals with isset like so:

if (isset($_SESSION['is_condition'])) {
//Do Whatever
}

Or you can do something unweildy like putting this at the top of your file.

if (isset($_SESSION['is_condition'])) {
//Do something
} else {
$_SESSION['is_condition'] = NULL;
}

From then on anytime you check the session for that key it will at least be set to something and wont throw a warning or notice. It's ugly but it will work.


问:
Cannot find Lombok plugin in Android Studio 2021.1.1 (build of 2022)
After installation of newest Android Studio I tried to install Lombok plugin
(Android Studio Bumblebee 2021.1.1 | Built on January 19, 2022)

But didn't find Lombok in Settings -> Plugins -> Marketplace

I found that the problem is:

Plugin 'Lombok' is not compatible with Android Studio build AI-211.7628.21

答:
After downloading the plugin file, version number needs to be changed.

I downloaded lombok-plugin-0.34-2020.2.zip from the releases (https://github.com/mplushnikov/lombok-intellij-plugin/releases), unzip it, went to the META-INF directory, open the jar file, changed in the xml file the line to <idea-version since-build="191.6183" until-build="IE-212.*"/> then saved the zip, installed the plugin from disk on Android Studio. Things seem to work - previous errors regarding unresolved getter functions now gone. Pretty brute force. Not sure if there will be side effects, but who knows if this plugin will only be updated after next year?

Edit: The best solution is to get rid of Lombok from your project.  It's very easy in Android Studio from Refactor->Delombok.


问:
How can I point multiple domain names to the same GitHub Pages site?
This seems like it should be a trivial task but isn't proving to be one for me...

How should I go about pointing multiple domain names to the same GitHub Pages hosted site?

Example:

I have created an account named test on GitHub and created a repository test.github.io

I bought test.com

I configured test.com's DNS as below

I have told GitHub Pages to enforce https and look out for custom domain test.com

My site is now live on test.com and www.test.com, woohoo!

DNS Config for test.com:

A @ 185.199.108.153 (github's nameserver)

A @ 185.199.109.153 (github's nameserver)

A @ 185.199.110.153 (github's nameserver)

A @ 185.199.111.153 (github's nameserver)

CNAME @ www.test.github.io (for www redirect)

I would ALSO like example.com (and a few other domains, foo.com, bar.com and foobar.com) to redirect to test.com

How do I do this?

With an apache server, this would be easy, be GitHub Pages only supports static sites.

Any ideas?

As always, thanks for any suggestions!

答:
I've accepted @Prahlad Yeri's answer from above, and am just leaving this here so that other's who stumble onto this question can easily figure out how to do this... As mentioned "The ideal place to have this redirect is your DNS provider"

To do this with domains purchased from domain dot com it is VERY simple, and even INSTANTANEOUS!  I can imagine with GoDaddy or other providers it will be similar.

On Domain dot com's control panel:

Log into account for the domain you wish to redirect

Go to "Pointers and Subdomains" on the left sidebar

Choose either "URL Standard" or "URL Stealth" from the pointer options, then enter the desired redirect URL in the "Directory" field

Press Save and you're good to go, immediately!

URL Stealth means that your URL will remain as what the user has typed in, and URL standard means it will display the URL of the site that you've directed to.  More documentation on pointers and subdomains (for domain dot com) can be found at domain.com/help/article/domain-management-how-to-update-domain-pointers


问:
Cypress reload page until element visible
I have following case:

After user uploads a file, there is a table report in the screen. The problem is: the table does not come up automatically, user needs to reload the page and if the file was already processed, the table is shown in screen, if it is still processing, user needs to wait a little bit and reload again.

How do I accomplish this task without using cy.wait() to wait an arbitrary time. I thought something like:

```<code>    cy.reload().should(() => {
        expect(document.querySelectorAll('table')).to.not.be.empty
    })
</code>```

but didn't work

答:
Perform the wait on the reload using recursion. Something like this:

```<code>function reloadPageUntilReportIsGenerated(maxAttempts=10, attempts=0) {
    if (attempts > maxAttempts) {
        throw new Error("Timed out waiting for report to be generated")
    }
    cy.get("table").then($table => {
        if ($table.children().length == 0) {  // do your condition check synchronously
            cy.reload()
            reloadPageUntilReportIsGenerated(maxAttempts, attempts+1)
        }
    })
}
</code>```

Then in the test call the wait right after you trigger the loading of the report.

```<code>cy.get("trigger-report").click()  // Start report generation
reloadPageUntilReportIsGenerated()  // Wait until page has report ready
</code>```

The main caveat to watch out for is the recursive waiting requires mixing asynchronous and synchronous code, so be careful or you'll end up with the synchronous code running in the wrong place (like right as the test begins). Avoid this by wrapping any synchronous calls inside the asynchronous ones using .then().

Body:
I have following case:

After user uploads a file, there is a table report in the screen. The problem is: the table does not come up automatically, user needs to reload the page and if the file was already processed, the table is shown in screen, if it is still processing, user needs to wait a little bit and reload again.

How do I accomplish this task without using cy.wait() to wait an arbitrary time. I thought something like:

    cy.reload().should(() => {
        expect(document.querySelectorAll('table')).to.not.be.empty
    })

but didn't work

Body:
Perform the wait on the reload using recursion. Something like this:

function reloadPageUntilReportIsGenerated(maxAttempts=10, attempts=0) {
    if (attempts > maxAttempts) {
        throw new Error("Timed out waiting for report to be generated")
    }
    cy.get("table").then($table => {
        if ($table.children().length == 0) {  // do your condition check synchronously
            cy.reload()
            reloadPageUntilReportIsGenerated(maxAttempts, attempts+1)
        }
    })
}

Then in the test call the wait right after you trigger the loading of the report.

cy.get("trigger-report").click()  // Start report generation
reloadPageUntilReportIsGenerated()  // Wait until page has report ready

The main caveat to watch out for is the recursive waiting requires mixing asynchronous and synchronous code, so be careful or you'll end up with the synchronous code running in the wrong place (like right as the test begins). Avoid this by wrapping any synchronous calls inside the asynchronous ones using .then().


问:
Issue with Javascript performance (Odin Project, Etch-a-Sketch)
I'm currently learning Javascript on the Odin Project and I'm doing the Etch-a-Sketch exercise in which you're supposed to create a Etch-a-Sketch board you can draw on with your cursor. You're supposed to implement a resize button which allows the user to enter how many tiles they want on each side of the board, with a max value of 100 (for a 100x100 board). I did just that, and it technically works... except my browser starts chugging around 60 tiles, and crashed if I try to go for the max.

The exercise teaches you how to use Javascript to manipulate the DOM, so I got my script to create a div container for the entire board, and a default 16x16 board is created when starting the page. Each tile is a div with an event listener assigned to it which detects when it's hovered. A new class is given to the tile, which changes its color according to the CSS rule tied to that class.

The reset button calls a function when it's clicked. That function resets the classes for all tiles and prompts the user for a value. Once it gets a proper value, the CSS rule associated with the original div container gets updated with the requested amount of rows and columns, a function is called which removes all existing tiles, and another function fills the div container with new tiles.

HTML

```<code><!DOCTYPE html>
<html>
<head>
  <title>Page Title</title>
  <meta charset="UTF-8"/>
  <link rel="stylesheet" href="style.css">
  <script src="script.js" defer></script>
</head>
<body>
  <button class="reset">Reset and resize</button>
</body>
</html>
</code>```

CSS

```<code>body {
    display: flex;
    flex-direction: column;
}

.reset {
    align-self: flex-start;
}

.sketchContainer {
    width: 900px;
    height: 900px;
    border: solid 1px black;
    display: grid;
    align-self: center;
    grid-template-rows: repeat(16, auto);
    grid-template-columns: repeat(16, auto);
}
div.tileHovered {
    background-color: #c3a375;
}
</code>```

JavaScript

```<code>generateDefaultGrid();
placeEventListenersOnTiles();
const button = document.querySelector(".reset");
button.addEventListener("click", reset);

function generateDefaultGrid() {
    const sketchContainer = document.createElement("div");
    document.body.appendChild(sketchContainer);
    sketchContainer.classList.add("sketchContainer");
    const gridTile = document.createElement("div");
    sketchContainer.appendChild(gridTile);
    gridTile.classList.add("gridTile");
    let clonedTile;
    for (let i = 0; i < 16*16-1; i++) {
        clonedTile = gridTile.cloneNode();                  //On ne peut pas copier un node en répétant les appendChild, autrement ça ne manipulera qu'une seule instance du node. IL faut le cloner.
        sketchContainer.appendChild(clonedTile);
    }
}

function placeEventListenersOnTiles() {
    let allTiles = document.querySelectorAll(".gridTile");
    allTiles.forEach(element => {
        element.addEventListener("mouseover", changeColor);
    });
}

function changeColor() {
    this.classList.add("tileHovered");                      //"this" permet de faire référence à l'élément sur lequel le listener a été placé.
}

function reset() {
    let allTiles = document.querySelectorAll(".tileHovered");
    allTiles.forEach(element => {
        element.classList.remove("tileHovered");
    });
    resizeGrid();
}

function resizeGrid() {
    let newSize = prompt("How many squares do you want per side of the Etch-a-Sketch?", "Please insert a number inferior or equal to 100.");
    if (newSize > 100) {
        newSize = prompt("Sorry, that number is too high!", "Please insert a number inferior or equal to 100.");
    } else if (Math.sign(newSize) == 0 || Math.sign(newSize) == -1) {                                                                           //Ne pas oublier que les returns de prompt sont des strings, pas des chiffres !
        newSize = prompt("Only integers between 1 and 100, please!", "Please insert a number inferior or equal to 100.");
    } else if (newSize === null) {
        generateDefaultGrid();
    } else {
        const styleSheet = document.styleSheets[0];
        styleSheet.cssRules[2].style.gridTemplateRows=`repeat(${newSize}, auto)`;
        styleSheet.cssRules[2].style.gridTemplateColumns=`repeat(${newSize}, auto)`;
        removeAllTiles();
        generateUserGrid(newSize);
    }
}

function removeAllTiles() {
    let container = document.querySelector(".sketchContainer");
    while (container.firstChild) {
        container.removeChild(container.firstChild);
    }
}

function generateUserGrid(newSize) {
    const sketchContainer = document.getElementsByClassName("sketchContainer");
    const gridTile = document.createElement("div");
    sketchContainer[0].appendChild(gridTile);
    gridTile.classList.add("gridTile");
    let clonedTile;
    for (let i = 0; i < newSize*newSize-1; i++) {
        clonedTile = gridTile.cloneNode();                  //On ne peut pas copier un node en répétant les appendChild, autrement ça ne manipulera qu'une seule instance du node. IL faut le cloner.
        sketchContainer[0].appendChild(clonedTile);
        placeEventListenersOnTiles();
    }
}
</code>```

I hope I'm doing the whole asking questions right, I'm pretty new to this. I've got the current state of my project pushed to my GitHub if it helps. Can someone tell me why my script is so inefficient, and how I could go about to fix it? That sort of thing wasn't really covered in the course up to that point, I'm afraid.

Thanks a lot!

答:
The performance problem is caused by placeEventListenersOnTiles(); being called within the for loop in generateUserGrid function.

So each iteration adds more and more event listeners again and again.

E.g. in 100x100 setup the first tile ends up with 10000 event listeners, the second one with 9999, and so on.

Just move it outside of the loop:

generateDefaultGrid();
placeEventListenersOnTiles();
const button = document.querySelector(".reset");
button.addEventListener("click", reset);

function generateDefaultGrid() {
  const sketchContainer = document.createElement("div");
  document.body.appendChild(sketchContainer);
  sketchContainer.classList.add("sketchContainer");
  const gridTile = document.createElement("div");
  sketchContainer.appendChild(gridTile);
  gridTile.classList.add("gridTile");
  let clonedTile;
  for (let i = 0; i < 16 * 16 - 1; i++) {
    clonedTile = gridTile.cloneNode(); //On ne peut pas copier un node en répétant les appendChild, autrement ça ne manipulera qu'une seule instance du node. IL faut le cloner.
    sketchContainer.appendChild(clonedTile);
  }
}

function placeEventListenersOnTiles() {
  let allTiles = document.querySelectorAll(".gridTile");
  allTiles.forEach(element => {
    element.addEventListener("mouseover", changeColor);
  });
}

function changeColor() {
  this.classList.add("tileHovered"); //"this" permet de faire référence à l'élément sur lequel le listener a été placé.
}

function reset() {
  let allTiles = document.querySelectorAll(".tileHovered");
  allTiles.forEach(element => {
    element.classList.remove("tileHovered");
  });
  resizeGrid();
}

function resizeGrid() {
  let newSize = prompt("How many squares do you want per side of the Etch-a-Sketch?", "Please insert a number inferior or equal to 100.");
  if (newSize > 100) {
    newSize = prompt("Sorry, that number is too high!", "Please insert a number inferior or equal to 100.");
  } else if (Math.sign(newSize) == 0 || Math.sign(newSize) == -1) { //Ne pas oublier que les returns de prompt sont des strings, pas des chiffres !
    newSize = prompt("Only integers between 1 and 100, please!", "Please insert a number inferior or equal to 100.");
  } else if (newSize === null) {
    generateDefaultGrid();
  } else {
    const styleSheet = document.styleSheets[0];
    styleSheet.cssRules[2].style.gridTemplateRows = `repeat(${newSize}, auto)`;
    styleSheet.cssRules[2].style.gridTemplateColumns = `repeat(${newSize}, auto)`;
    removeAllTiles();
    generateUserGrid(newSize);
  }
}

function removeAllTiles() {
  let container = document.querySelector(".sketchContainer");
  while (container.firstChild) {
    container.removeChild(container.firstChild);
  }
}

function generateUserGrid(newSize) {
  const sketchContainer = document.getElementsByClassName("sketchContainer");
  const gridTile = document.createElement("div");
  sketchContainer[0].appendChild(gridTile);
  gridTile.classList.add("gridTile");
  let clonedTile;
  for (let i = 0; i < newSize * newSize - 1; i++) {
    clonedTile = gridTile.cloneNode(); //On ne peut pas copier un node en répétant les appendChild, autrement ça ne manipulera qu'une seule instance du node. IL faut le cloner.
    sketchContainer[0].appendChild(clonedTile);
  }
  placeEventListenersOnTiles();
}```
<pre class="snippet-code-css lang-css prettyprint-override"><code>body {
  display: flex;
  flex-direction: column;
}

.reset {
  align-self: flex-start;
}

.sketchContainer {
  width: 450px;
  height: 450px;
  border: solid 1px black;
  display: grid;
  align-self: center;
  grid-template-rows: repeat(16, auto);
  grid-template-columns: repeat(16, auto);
}

div.tileHovered {
  background-color: #c3a375;
}</code>```

<button class="reset">Reset and resize</button>```
</div>
</div>
</p>
<p>Probably you want to go further and learn event delegation. So you'd have just 1 event listener set on the container to handle all the tiles:</p>
<p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
<div class="snippet-code">
<pre class="snippet-code-js lang-js prettyprint-override"><code>generateDefaultGrid();
const button = document.querySelector(".reset");
button.addEventListener("click", reset);

function generateDefaultGrid() {
  const sketchContainer = document.createElement("div");
  document.body.appendChild(sketchContainer);
  sketchContainer.classList.add("sketchContainer");

  // SEE HERE:
  sketchContainer.addEventListener("mouseover", changeColor);

  const gridTile = document.createElement("div");
  sketchContainer.appendChild(gridTile);
  gridTile.classList.add("gridTile");
  let clonedTile;
  for (let i = 0; i < 16 * 16 - 1; i++) {
    clonedTile = gridTile.cloneNode(); //On ne peut pas copier un node en répétant les appendChild, autrement ça ne manipulera qu'une seule instance du node. IL faut le cloner.
    sketchContainer.appendChild(clonedTile);
  }
}

// AND HERE:
function changeColor(event) {
  event.target.classList.add("tileHovered");
}

function reset() {
  let allTiles = document.querySelectorAll(".tileHovered");
  allTiles.forEach(element => {
    element.classList.remove("tileHovered");
  });
  resizeGrid();
}

function resizeGrid() {
  let newSize = prompt("How many squares do you want per side of the Etch-a-Sketch?", "Please insert a number inferior or equal to 100.");
  if (newSize > 100) {
    newSize = prompt("Sorry, that number is too high!", "Please insert a number inferior or equal to 100.");
  } else if (Math.sign(newSize) == 0 || Math.sign(newSize) == -1) { //Ne pas oublier que les returns de prompt sont des strings, pas des chiffres !
    newSize = prompt("Only integers between 1 and 100, please!", "Please insert a number inferior or equal to 100.");
  } else if (newSize === null) {
    generateDefaultGrid();
  } else {
    const styleSheet = document.styleSheets[0];
    styleSheet.cssRules[2].style.gridTemplateRows = `repeat(${newSize}, auto)`;
    styleSheet.cssRules[2].style.gridTemplateColumns = `repeat(${newSize}, auto)`;
    removeAllTiles();
    generateUserGrid(newSize);
  }
}

function removeAllTiles() {
  let container = document.querySelector(".sketchContainer");
  while (container.firstChild) {
    container.removeChild(container.firstChild);
  }
}

function generateUserGrid(newSize) {
  const sketchContainer = document.getElementsByClassName("sketchContainer");
  const gridTile = document.createElement("div");
  sketchContainer[0].appendChild(gridTile);
  gridTile.classList.add("gridTile");
  let clonedTile;
  for (let i = 0; i < newSize * newSize - 1; i++) {
    clonedTile = gridTile.cloneNode(); //On ne peut pas copier un node en répétant les appendChild, autrement ça ne manipulera qu'une seule instance du node. IL faut le cloner.
    sketchContainer[0].appendChild(clonedTile);
  }
}</code>```

body {
  display: flex;
  flex-direction: column;
}

.reset {
  align-self: flex-start;
}

.sketchContainer {
  width: 450px;
  height: 450px;
  border: solid 1px black;
  display: grid;
  align-self: center;
  grid-template-rows: repeat(16, auto);
  grid-template-columns: repeat(16, auto);
}

div.tileHovered {
  background-color: #c3a375;
}```
<pre class="snippet-code-html lang-html prettyprint-override"><code><button class="reset">Reset and resize</button></code>```

Body:
I'm currently learning Javascript on the Odin Project and I'm doing the Etch-a-Sketch exercise in which you're supposed to create a Etch-a-Sketch board you can draw on with your cursor. You're supposed to implement a resize button which allows the user to enter how many tiles they want on each side of the board, with a max value of 100 (for a 100x100 board). I did just that, and it technically works... except my browser starts chugging around 60 tiles, and crashed if I try to go for the max.

The exercise teaches you how to use Javascript to manipulate the DOM, so I got my script to create a div container for the entire board, and a default 16x16 board is created when starting the page. Each tile is a div with an event listener assigned to it which detects when it's hovered. A new class is given to the tile, which changes its color according to the CSS rule tied to that class.

The reset button calls a function when it's clicked. That function resets the classes for all tiles and prompts the user for a value. Once it gets a proper value, the CSS rule associated with the original div container gets updated with the requested amount of rows and columns, a function is called which removes all existing tiles, and another function fills the div container with new tiles.

HTML

<!DOCTYPE html>

  <title>Page Title</title>
  <meta charset="UTF-8"/>
  <link rel="stylesheet" href="style.css">

  <button class="reset">Reset and resize</button>

CSS

body {
    display: flex;
    flex-direction: column;
}

.reset {
    align-self: flex-start;
}

.sketchContainer {
    width: 900px;
    height: 900px;
    border: solid 1px black;
    display: grid;
    align-self: center;
    grid-template-rows: repeat(16, auto);
    grid-template-columns: repeat(16, auto);
}
div.tileHovered {
    background-color: #c3a375;
}

JavaScript

generateDefaultGrid();
placeEventListenersOnTiles();
const button = document.querySelector(".reset");
button.addEventListener("click", reset);

function generateDefaultGrid() {
    const sketchContainer = document.createElement("div");
    document.body.appendChild(sketchContainer);
    sketchContainer.classList.add("sketchContainer");
    const gridTile = document.createElement("div");
    sketchContainer.appendChild(gridTile);
    gridTile.classList.add("gridTile");
    let clonedTile;
    for (let i = 0; i < 16*16-1; i++) {
        clonedTile = gridTile.cloneNode();                  //On ne peut pas copier un node en répétant les appendChild, autrement ça ne manipulera qu'une seule instance du node. IL faut le cloner.
        sketchContainer.appendChild(clonedTile);
    }
}

function placeEventListenersOnTiles() {
    let allTiles = document.querySelectorAll(".gridTile");
    allTiles.forEach(element => {
        element.addEventListener("mouseover", changeColor);
    });
}

function changeColor() {
    this.classList.add("tileHovered");                      //"this" permet de faire référence à l'élément sur lequel le listener a été placé.
}

function reset() {
    let allTiles = document.querySelectorAll(".tileHovered");
    allTiles.forEach(element => {
        element.classList.remove("tileHovered");
    });
    resizeGrid();
}

function resizeGrid() {
    let newSize = prompt("How many squares do you want per side of the Etch-a-Sketch?", "Please insert a number inferior or equal to 100.");
    if (newSize > 100) {
        newSize = prompt("Sorry, that number is too high!", "Please insert a number inferior or equal to 100.");
    } else if (Math.sign(newSize) == 0 || Math.sign(newSize) == -1) {                                                                           //Ne pas oublier que les returns de prompt sont des strings, pas des chiffres !
        newSize = prompt("Only integers between 1 and 100, please!", "Please insert a number inferior or equal to 100.");
    } else if (newSize === null) {
        generateDefaultGrid();
    } else {
        const styleSheet = document.styleSheets[0];
        styleSheet.cssRules[2].style.gridTemplateRows=`repeat(${newSize}, auto)`;
        styleSheet.cssRules[2].style.gridTemplateColumns=`repeat(${newSize}, auto)`;
        removeAllTiles();
        generateUserGrid(newSize);
    }
}

function removeAllTiles() {
    let container = document.querySelector(".sketchContainer");
    while (container.firstChild) {
        container.removeChild(container.firstChild);
    }
}

function generateUserGrid(newSize) {
    const sketchContainer = document.getElementsByClassName("sketchContainer");
    const gridTile = document.createElement("div");
    sketchContainer[0].appendChild(gridTile);
    gridTile.classList.add("gridTile");
    let clonedTile;
    for (let i = 0; i < newSize*newSize-1; i++) {
        clonedTile = gridTile.cloneNode();                  //On ne peut pas copier un node en répétant les appendChild, autrement ça ne manipulera qu'une seule instance du node. IL faut le cloner.
        sketchContainer[0].appendChild(clonedTile);
        placeEventListenersOnTiles();
    }
}

I hope I'm doing the whole asking questions right, I'm pretty new to this. I've got the current state of my project pushed to my GitHub if it helps. Can someone tell me why my script is so inefficient, and how I could go about to fix it? That sort of thing wasn't really covered in the course up to that point, I'm afraid.

Thanks a lot!

Body:
The performance problem is caused by placeEventListenersOnTiles(); being called within the for loop in generateUserGrid function.

So each iteration adds more and more event listeners again and again.

E.g. in 100x100 setup the first tile ends up with 10000 event listeners, the second one with 9999, and so on.

Just move it outside of the loop:

generateDefaultGrid();
placeEventListenersOnTiles();
const button = document.querySelector(".reset");
button.addEventListener("click", reset);

function generateDefaultGrid() {
  const sketchContainer = document.createElement("div");
  document.body.appendChild(sketchContainer);
  sketchContainer.classList.add("sketchContainer");
  const gridTile = document.createElement("div");
  sketchContainer.appendChild(gridTile);
  gridTile.classList.add("gridTile");
  let clonedTile;
  for (let i = 0; i < 16 * 16 - 1; i++) {
    clonedTile = gridTile.cloneNode(); //On ne peut pas copier un node en répétant les appendChild, autrement ça ne manipulera qu'une seule instance du node. IL faut le cloner.
    sketchContainer.appendChild(clonedTile);
  }
}

function placeEventListenersOnTiles() {
  let allTiles = document.querySelectorAll(".gridTile");
  allTiles.forEach(element => {
    element.addEventListener("mouseover", changeColor);
  });
}

function changeColor() {
  this.classList.add("tileHovered"); //"this" permet de faire référence à l'élément sur lequel le listener a été placé.
}

function reset() {
  let allTiles = document.querySelectorAll(".tileHovered");
  allTiles.forEach(element => {
    element.classList.remove("tileHovered");
  });
  resizeGrid();
}

function resizeGrid() {
  let newSize = prompt("How many squares do you want per side of the Etch-a-Sketch?", "Please insert a number inferior or equal to 100.");
  if (newSize > 100) {
    newSize = prompt("Sorry, that number is too high!", "Please insert a number inferior or equal to 100.");
  } else if (Math.sign(newSize) == 0 || Math.sign(newSize) == -1) { //Ne pas oublier que les returns de prompt sont des strings, pas des chiffres !
    newSize = prompt("Only integers between 1 and 100, please!", "Please insert a number inferior or equal to 100.");
  } else if (newSize === null) {
    generateDefaultGrid();
  } else {
    const styleSheet = document.styleSheets[0];
    styleSheet.cssRules[2].style.gridTemplateRows = `repeat(${newSize}, auto)`;
    styleSheet.cssRules[2].style.gridTemplateColumns = `repeat(${newSize}, auto)`;
    removeAllTiles();
    generateUserGrid(newSize);
  }
}

function removeAllTiles() {
  let container = document.querySelector(".sketchContainer");
  while (container.firstChild) {
    container.removeChild(container.firstChild);
  }
}

function generateUserGrid(newSize) {
  const sketchContainer = document.getElementsByClassName("sketchContainer");
  const gridTile = document.createElement("div");
  sketchContainer[0].appendChild(gridTile);
  gridTile.classList.add("gridTile");
  let clonedTile;
  for (let i = 0; i < newSize * newSize - 1; i++) {
    clonedTile = gridTile.cloneNode(); //On ne peut pas copier un node en répétant les appendChild, autrement ça ne manipulera qu'une seule instance du node. IL faut le cloner.
    sketchContainer[0].appendChild(clonedTile);
  }
  placeEventListenersOnTiles();
}

body {
  display: flex;
  flex-direction: column;
}

.reset {
  align-self: flex-start;
}

.sketchContainer {
  width: 450px;
  height: 450px;
  border: solid 1px black;
  display: grid;
  align-self: center;
  grid-template-rows: repeat(16, auto);
  grid-template-columns: repeat(16, auto);
}

div.tileHovered {
  background-color: #c3a375;
}

<button class="reset">Reset and resize</button>

Probably you want to go further and learn event delegation. So you'd have just 1 event listener set on the container to handle all the tiles:

generateDefaultGrid();
const button = document.querySelector(".reset");
button.addEventListener("click", reset);

function generateDefaultGrid() {
  const sketchContainer = document.createElement("div");
  document.body.appendChild(sketchContainer);
  sketchContainer.classList.add("sketchContainer");

  // SEE HERE:
  sketchContainer.addEventListener("mouseover", changeColor);

  const gridTile = document.createElement("div");
  sketchContainer.appendChild(gridTile);
  gridTile.classList.add("gridTile");
  let clonedTile;
  for (let i = 0; i < 16 * 16 - 1; i++) {
    clonedTile = gridTile.cloneNode(); //On ne peut pas copier un node en répétant les appendChild, autrement ça ne manipulera qu'une seule instance du node. IL faut le cloner.
    sketchContainer.appendChild(clonedTile);
  }
}

// AND HERE:
function changeColor(event) {
  event.target.classList.add("tileHovered");
}

function reset() {
  let allTiles = document.querySelectorAll(".tileHovered");
  allTiles.forEach(element => {
    element.classList.remove("tileHovered");
  });
  resizeGrid();
}

function resizeGrid() {
  let newSize = prompt("How many squares do you want per side of the Etch-a-Sketch?", "Please insert a number inferior or equal to 100.");
  if (newSize > 100) {
    newSize = prompt("Sorry, that number is too high!", "Please insert a number inferior or equal to 100.");
  } else if (Math.sign(newSize) == 0 || Math.sign(newSize) == -1) { //Ne pas oublier que les returns de prompt sont des strings, pas des chiffres !
    newSize = prompt("Only integers between 1 and 100, please!", "Please insert a number inferior or equal to 100.");
  } else if (newSize === null) {
    generateDefaultGrid();
  } else {
    const styleSheet = document.styleSheets[0];
    styleSheet.cssRules[2].style.gridTemplateRows = `repeat(${newSize}, auto)`;
    styleSheet.cssRules[2].style.gridTemplateColumns = `repeat(${newSize}, auto)`;
    removeAllTiles();
    generateUserGrid(newSize);
  }
}

function removeAllTiles() {
  let container = document.querySelector(".sketchContainer");
  while (container.firstChild) {
    container.removeChild(container.firstChild);
  }
}

function generateUserGrid(newSize) {
  const sketchContainer = document.getElementsByClassName("sketchContainer");
  const gridTile = document.createElement("div");
  sketchContainer[0].appendChild(gridTile);
  gridTile.classList.add("gridTile");
  let clonedTile;
  for (let i = 0; i < newSize * newSize - 1; i++) {
    clonedTile = gridTile.cloneNode(); //On ne peut pas copier un node en répétant les appendChild, autrement ça ne manipulera qu'une seule instance du node. IL faut le cloner.
    sketchContainer[0].appendChild(clonedTile);
  }
}

body {
  display: flex;
  flex-direction: column;
}

.reset {
  align-self: flex-start;
}

.sketchContainer {
  width: 450px;
  height: 450px;
  border: solid 1px black;
  display: grid;
  align-self: center;
  grid-template-rows: repeat(16, auto);
  grid-template-columns: repeat(16, auto);
}

div.tileHovered {
  background-color: #c3a375;
}

<button class="reset">Reset and resize</button>


问:
Xcode Build react native code and images not finding node
I am trying to build react native ios release app but I am getting an error because Xcode can't see node binary,

in the Build react native code and images section in Xcode, I am exporting NODE_BINARY=node this way:

export EXTRA_PACKAGER_ARGS="--sourcemap-output $DERIVED_FILE_DIR/main.jsbundle.map"
export NODE_BINARY=node
../node_modules/react-native/scripts/react-native-xcode.sh

```
<p>but it didn't work because <code>Xcode</code> can't find node binary.</p>
<p>so when I changed <code>NODE_BINARY</code> from node to:</p>
<pre class="lang-js prettyprint-override"><code>export NODE_BINARY=/opt/homebrew/bin/node
</code>```

it works well this way, but I can't export it this way because the testers team have the node bin in another directory.

so I exported a variable in .zshrc file called NODE_CLI but I don't know how to use it in Xcode
for example:

```<code>export NODE_BINARY=$NODE_CLI
</code>```

or

```<code>export NODE_BINARY=$($NODE_CLI)
</code>```

or

```<code>export NODE_BINARY=${NODE_CLI}
</code>```

any suggestions?

thank you.

答:
instead of directly passing the node variable, you can leave it to xcode to discern which node you're using by implying it, do this instead:

export NODE_BINARY=$(which node)

Found pointers for this on several SO answers and it's given me no issues

Body:
I am trying to build react native ios release app but I am getting an error because Xcode can't see node binary,

in the Build react native code and images section in Xcode, I am exporting NODE_BINARY=node this way:

export EXTRA_PACKAGER_ARGS="--sourcemap-output $DERIVED_FILE_DIR/main.jsbundle.map"
export NODE_BINARY=node
../node_modules/react-native/scripts/react-native-xcode.sh

but it didn't work because Xcode can't find node binary.

so when I changed NODE_BINARY from node to:

export NODE_BINARY=/opt/homebrew/bin/node

it works well this way, but I can't export it this way because the testers team have the node bin in another directory.

so I exported a variable in .zshrc file called NODE_CLI but I don't know how to use it in Xcode
for example:

export NODE_BINARY=$NODE_CLI

or

export NODE_BINARY=$($NODE_CLI)

or

export NODE_BINARY=${NODE_CLI}

any suggestions?

thank you.


问:
Comparing math operator with string
I'm starting at web development and trying to create my first web app, a calculator, but I bumped into a bit of a problem.

I wanted an app where the user wrote the operator of the operation that needs to be performed and then the program would check this operator and run the specified equation. I know how to perform this in Python but I cannot use the same approach in JS in an HTML file.

In Python I would do something like this:

a = 4
b = 4
operator = '+'

if operator == '+':
    print(a+b)
if operator == '-':
    print(a-b)
```

答:
So I've got it to work. I was not getting the value as I was trying to compare it.

so I was trying with

```<code>if (operator == '+'):
   equation = value_b + value_a
</code>```

And it should be

```<code>if (operator.value == '+'):
   equation = value_b + value_a
</code>```

Title: Comparing math operator with string

Body:
So I've got it to work. I was not getting the value as I was trying to compare it.

so I was trying with

if (operator == '+'):
   equation = value_b + value_a

And it should be

if (operator.value == '+'):
   equation = value_b + value_a


问:
Py.GIL() is giving error pythonnet embedded python in C#
I have the following C# code:

```<code>static void Main()
        {

            string pythonpath1 = @"C:\Users\user\Documents\pynet_test\Python\Python37";
            string pythonpath2 = @"C:\Users\user\Documents\pynet_test\Python\Python37\lib";
            string envpythonhome = @"C:\Users\user\Documents\pynet_test\Python\Python37\python37.dll";

            Environment.SetEnvironmentVariable("PYTHONNET_PYDLL", envpythonhome, EnvironmentVariableTarget.Process);
            Environment.SetEnvironmentVariable("PATH", pythonpath1, EnvironmentVariableTarget.Process);
            Environment.SetEnvironmentVariable("PYTHONHOME", pythonpath1, EnvironmentVariableTarget.Process);
            Environment.SetEnvironmentVariable("PYTHONPATH", pythonpath2, EnvironmentVariableTarget.Process);

            using (Py.GIL())
            {
                dynamic np = Py.Import("numpy");
                Console.WriteLine(np.cos(np.pi * 2));

                dynamic sin = np.sin;
                Console.WriteLine(sin(5));

                double c = np.cos(5) + sin(5);
                Console.WriteLine(c);

                dynamic a = np.array(new List<float> { 1, 2, 3 });
                Console.WriteLine(a.dtype);

                dynamic b = np.array(new List<float> { 6, 5, 4 }, dtype: np.int32);
                Console.WriteLine(b.dtype);

                Console.WriteLine(a * b);
            }

            Console.WriteLine(DateTime.Now);

        }
</code>```

The error I am getting is:

```<code>System.MissingMethodException
  HResult=0x80131513
  Message=Method not found: 'System.Reflection.Emit.AssemblyBuilder System.AppDomain.DefineDynamicAssembly(System.Reflection.AssemblyName, System.Reflection.Emit.AssemblyBuilderAccess)'.
  Source=Python.Runtime
  StackTrace:
   at Python.Runtime.CodeGenerator..ctor()
   at Python.Runtime.DelegateManager..ctor()
   at Python.Runtime.PythonEngine.Initialize(IEnumerable`1 args, Boolean setSysArgv, Boolean initSigs)
   at Python.Runtime.PythonEngine.Initialize(Boolean setSysArgv, Boolean initSigs)
   at Python.Runtime.PythonEngine.Initialize()
   at Python.Runtime.Py.GIL()
   at WrapperPython.Program.Main() in C:\Users\user\Documents\pynet_test\pynet_test\Program.cs:line 50
</code>```

The Python environment is in the Project folder and this is the following specifications:

python version used : 3.7 (x64)

pythonnet version: 2.5.2-cp37-cp37m-win_amd64

OS : Windows Server 2019

Reference has been made to the python.Runtime.dll under site-packaged. CSproj looks like following:

```<code><Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
      <LangVersion>latest</LangVersion>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <PlatformTarget>x64</PlatformTarget>
  </PropertyGroup>

  <ItemGroup>
    <Reference Include="Python.Runtime">
      <HintPath>..\Python\Python37\Lib\site-packages\Python.Runtime.dll</HintPath>
    </Reference>
  </ItemGroup>

</Project>
</code>```

I have tried everything provided online but can't seem to find the issue. I have a hunch it's based on the environment variables but not sure how to proceed.

答:
okay so python.net installation is really not well documented and the folks maintaining the python.net repository don't really help a lot since it's not a "support forum".

I solved this issue by installing the python.runtime.AllPlatflorms nuget package and pointing the environment variables to the right python folders/ files.

This works with python3.8 as well.

Body:
I have the following C# code:

static void Main()
        {

            string pythonpath1 = @"C:\Users\user\Documents\pynet_test\Python\Python37";
            string pythonpath2 = @"C:\Users\user\Documents\pynet_test\Python\Python37\lib";
            string envpythonhome = @"C:\Users\user\Documents\pynet_test\Python\Python37\python37.dll";

            Environment.SetEnvironmentVariable("PYTHONNET_PYDLL", envpythonhome, EnvironmentVariableTarget.Process);
            Environment.SetEnvironmentVariable("PATH", pythonpath1, EnvironmentVariableTarget.Process);
            Environment.SetEnvironmentVariable("PYTHONHOME", pythonpath1, EnvironmentVariableTarget.Process);
            Environment.SetEnvironmentVariable("PYTHONPATH", pythonpath2, EnvironmentVariableTarget.Process);

            using (Py.GIL())
            {
                dynamic np = Py.Import("numpy");
                Console.WriteLine(np.cos(np.pi * 2));

                dynamic sin = np.sin;
                Console.WriteLine(sin(5));

                double c = np.cos(5) + sin(5);
                Console.WriteLine(c);

                dynamic a = np.array(new List<float> { 1, 2, 3 });
                Console.WriteLine(a.dtype);

                dynamic b = np.array(new List<float> { 6, 5, 4 }, dtype: np.int32);
                Console.WriteLine(b.dtype);

                Console.WriteLine(a * b);
            }

            Console.WriteLine(DateTime.Now);

        }

The error I am getting is:

System.MissingMethodException
  HResult=0x80131513
  Message=Method not found: 'System.Reflection.Emit.AssemblyBuilder System.AppDomain.DefineDynamicAssembly(System.Reflection.AssemblyName, System.Reflection.Emit.AssemblyBuilderAccess)'.
  Source=Python.Runtime
  StackTrace:
   at Python.Runtime.CodeGenerator..ctor()
   at Python.Runtime.DelegateManager..ctor()
   at Python.Runtime.PythonEngine.Initialize(IEnumerable`1 args, Boolean setSysArgv, Boolean initSigs)
   at Python.Runtime.PythonEngine.Initialize(Boolean setSysArgv, Boolean initSigs)
   at Python.Runtime.PythonEngine.Initialize()
   at Python.Runtime.Py.GIL()
   at WrapperPython.Program.Main() in C:\Users\user\Documents\pynet_test\pynet_test\Program.cs:line 50

The Python environment is in the Project folder and this is the following specifications:

python version used : 3.7 (x64)

pythonnet version: 2.5.2-cp37-cp37m-win_amd64

OS : Windows Server 2019

Reference has been made to the python.Runtime.dll under site-packaged. CSproj looks like following:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
      <LangVersion>latest</LangVersion>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <PlatformTarget>x64</PlatformTarget>
  </PropertyGroup>

  <ItemGroup>
    <Reference Include="Python.Runtime">
      <HintPath>..\Python\Python37\Lib\site-packages\Python.Runtime.dll</HintPath>
    </Reference>
  </ItemGroup>

</Project>

I have tried everything provided online but can't seem to find the issue. I have a hunch it's based on the environment variables but not sure how to proceed.