Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uli comm backend #660

Merged
merged 3 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
create crowdsource context & add login in plugin
  • Loading branch information
maanasb01 committed Jan 20, 2025
commit 123b2076e81bd813002a79d36e6b468dc51ec6ce
2 changes: 1 addition & 1 deletion browser-extension/plugin/.env.development
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ENVIRONMENT=development
LOCAL_STORAGE_NAME=ogbvData
API_URL=http://localhost:3000
API_URL=http://localhost:4000
MEDIA_URL=https://uli-media.tattle.co.in
21 changes: 21 additions & 0 deletions browser-extension/plugin/src/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import axios from "axios";
import config from "./config";

const { API_URL } = config;

export async function userLogin({email, password}){

console.log("INSIDE USERLOGIN: ")
try {
const response = await axios.post(`${API_URL}/api/auth/login`, {
email,
password
});
console.log("RESPONSE IS: ",response)
console.log('Login successful:', response.data);
return response.data;
} catch (error) {

throw error;
}
}
1 change: 1 addition & 0 deletions browser-extension/plugin/src/ui-components/pages/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export function App() {
async function navigatePreferences() {
try {
const userData = await getUserData();
console.log("USER DATA: ", userData)
const preferenceData = await getPreferenceData();

if (!ignore) {
Expand Down
129 changes: 107 additions & 22 deletions browser-extension/plugin/src/ui-components/pages/Debug.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
TextInput,
Heading
} from 'grommet';
import { UserContext } from '../atoms/AppContext';
import { UserContext, NotificationContext } from '../atoms/AppContext';
import repository from '../../repository';
import config from '../../config';
import { useTranslation } from 'react-i18next';
Expand All @@ -18,41 +18,126 @@ const { getUserData, getPreferenceData, setUserData, setPreferenceData } =
repository;
const { resetAccount } = Api;
import { Hide, View } from 'grommet-icons';
import { userLogin } from '../../api';

export function Debug() {
const { user, setUser } = useContext(UserContext);
const { showNotification } = useContext(NotificationContext);

const [localStorageData, setLocalStorageData] = useState(undefined);
const { t, i18n } = useTranslation();
const [isResetChecked, setIsResetChecked] = useState(false);

useEffect(() => {
async function localStorage() {
const userData = await getUserData();
const preferenceData = await getPreferenceData();
if (!ignore) {
setLocalStorageData({
user: userData,
preference: preferenceData
});
}
}
let ignore = false;
localStorage();
return () => {
ignore = true;
};
}, []);

return <Box>{user ? <Box>Logged in. Settings</Box> : <LoginForm />}</Box>;
// useEffect(() => {
// async function localStorage() {
// const userData = await getUserData();
// const preferenceData = await getPreferenceData();
// if (!ignore) {
// setLocalStorageData({
// user: userData,
// preference: preferenceData
// });
// }
// }
// let ignore = false;
// localStorage();
// return () => {
// ignore = true;
// };
// }, []);

return (
<Box>
{user ? (
<Box>
<Text>
Hello, <b>{user?.email}</b> !
</Text>
<Box
pad={'small'}
border={{ color: 'status-critical' }}
margin={{ top: 'xsmall' }}
fill={'horizontal'}
align="start"
>
<Text color={'status-critical'}>
Logout
</Text>
<Box height={'0.8em'}></Box>
<Box gap={'small'}>
<CheckBox
checked={isResetChecked}
label={"I am sure I want to logout from this account"}
onChange={(e) =>
setIsResetChecked(e.target.checked)
}
/>
<Button
label={"Logout"}
disabled={!isResetChecked}
secondary
onClick={async()=>{
await setUserData(undefined);
setUser(null);
}}
/>
</Box>
</Box>
</Box>
) : (
<LoginForm />
)}
</Box>
);
}

const LoginForm = () => {
const [reveal, setReveal] = useState(false);
const [formValues, setFormValues] = useState({ email: '', password: '' });
const { showNotification } = useContext(NotificationContext);
const { user, setUser } = useContext(UserContext);

const handleSubmit = ({ value }) => {
async function handleSubmit({ value }) {
console.log('Form Submitted:', value);
};

try {
let data = await userLogin(value);

console.log('LOGIN SUCCESS: ', data);
showNotification({
type: 'info',
message: 'Login Successful'
});

const { email, token } = data;

let setData = { email, token };

await setUserData(setData);
setUser(setData);
} catch (error) {
console.error(error);
if (error?.response?.status === 401) {
// console.log('UNAUTHORIZED', error);
showNotification({
type: 'error',
message: 'Unauthorized'
});
} else if (error?.response?.status >= 500) {
// console.log('UNAUTHORIZED', error);
showNotification({
type: 'error',
message: 'Server Error'
});
} else {
// console.log('SOMETHING WENT WRONG', error);
showNotification({
type: 'error',
message: 'Something Went Wrong'
});
}
}
}

return (
<Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function SlurCreate() {
let newValue = slurCreatePluginToApi(value);
// console.log(newValue);
try {
await createSlurAndCategory(user.accessToken, newValue);
await createSlurAndCategory("dea07e31-417c-4547-9208-57ff7fcf2da8", newValue);
navigate('/slur');
showNotification({
type: 'message',
Expand Down
104 changes: 104 additions & 0 deletions uli-community/lib/uli_community/user_contribution.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
defmodule UliCommunity.UserContribution do
@moduledoc """
The UserContribution context.
"""

import Ecto.Query, warn: false
alias UliCommunity.Repo

alias UliCommunity.UserContribution.CrowdsourcedSlur

@doc """
Returns the list of crowdsourced_slurs.

## Examples

iex> list_crowdsourced_slurs()
[%CrowdsourcedSlur{}, ...]

"""
def list_crowdsourced_slurs do
Repo.all(CrowdsourcedSlur)
end

@doc """
Gets a single crowdsourced_slur.

Raises `Ecto.NoResultsError` if the Crowdsourced slur does not exist.

## Examples

iex> get_crowdsourced_slur!(123)
%CrowdsourcedSlur{}

iex> get_crowdsourced_slur!(456)
** (Ecto.NoResultsError)

"""
def get_crowdsourced_slur!(id), do: Repo.get!(CrowdsourcedSlur, id)

@doc """
Creates a crowdsourced_slur.

## Examples

iex> create_crowdsourced_slur(%{field: value})
{:ok, %CrowdsourcedSlur{}}

iex> create_crowdsourced_slur(%{field: bad_value})
{:error, %Ecto.Changeset{}}

"""
def create_crowdsourced_slur(attrs \\ %{}) do
%CrowdsourcedSlur{}
|> CrowdsourcedSlur.changeset(attrs)
|> Repo.insert()
end

@doc """
Updates a crowdsourced_slur.

## Examples

iex> update_crowdsourced_slur(crowdsourced_slur, %{field: new_value})
{:ok, %CrowdsourcedSlur{}}

iex> update_crowdsourced_slur(crowdsourced_slur, %{field: bad_value})
{:error, %Ecto.Changeset{}}

"""
def update_crowdsourced_slur(%CrowdsourcedSlur{} = crowdsourced_slur, attrs) do
crowdsourced_slur
|> CrowdsourcedSlur.changeset(attrs)
|> Repo.update()
end

@doc """
Deletes a crowdsourced_slur.

## Examples

iex> delete_crowdsourced_slur(crowdsourced_slur)
{:ok, %CrowdsourcedSlur{}}

iex> delete_crowdsourced_slur(crowdsourced_slur)
{:error, %Ecto.Changeset{}}

"""
def delete_crowdsourced_slur(%CrowdsourcedSlur{} = crowdsourced_slur) do
Repo.delete(crowdsourced_slur)
end

@doc """
Returns an `%Ecto.Changeset{}` for tracking crowdsourced_slur changes.

## Examples

iex> change_crowdsourced_slur(crowdsourced_slur)
%Ecto.Changeset{data: %CrowdsourcedSlur{}}

"""
def change_crowdsourced_slur(%CrowdsourcedSlur{} = crowdsourced_slur, attrs \\ %{}) do
CrowdsourcedSlur.changeset(crowdsourced_slur, attrs)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
defmodule UliCommunity.UserContribution.CrowdsourcedSlur do
use Ecto.Schema
import Ecto.Changeset

schema "crowdsourced_slurs" do
field :label, :string
field :category, {:array, :string}
field :level_of_severity, Ecto.Enum, values: [:low, :medium, :high]
field :casual, :boolean, default: false
field :appropriated, :boolean, default: false
field :appropriation_context, :boolean, default: false
field :meaning, :string

timestamps(type: :utc_datetime)
end

@doc false
def changeset(crowdsourced_slur, attrs) do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A tip. There's a few ways to create crowdsouced_slur

  1. Only add the label and none of the other fields
  2. Add label and one or more of the other fields
  3. Add/update values like category, level_of_severity etc for an already existing slur

You might need more than one changeset to capture these possibilities.

Conventionally these functions should be named changeset_*. We can discuss appropriate names later.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, yeah sure I will add more methods to create an entry with different scenarios. This was generated by the Phoenix generator. I was planning to add these on a requirement basis when I integrate the FE and BE.

Regarding this, just want to confirm, when I see the "add slur" form, I see that apart from appropriation_context and "what makes it problematic" fields, all other fields are necessary, so should I only create the changeset regarding these?

crowdsourced_slur
|> cast(attrs, [:label, :level_of_severity, :casual, :appropriated, :appropriation_context, :meaning, :category])
|> validate_required([:label, :level_of_severity, :casual, :appropriated, :appropriation_context, :meaning, :category])
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ defmodule UliCommunityWeb.SessionControllerApi do
user ->
IO.inspect(user, label: "USER IS: ")
with {:ok, token} <- Token.sign(%{user_id: user.id}) do
json(conn, %{token: token, message: "Token Generation Successful!"})
json(conn, %{token: token, message: "Token Generation Successful!", email: user.email})
else
_ ->
conn
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
defmodule UliCommunity.Repo.Migrations.CreateCrowdsourcedSlurs do
use Ecto.Migration

def change do

execute("CREATE TYPE level_of_severity AS ENUM ('low', 'medium', 'high')")
create table(:crowdsourced_slurs) do
add :label, :string
add :level_of_severity, :level_of_severity
add :casual, :boolean, default: false, null: false
add :appropriated, :boolean, default: false, null: false
add :appropriation_context, :boolean, default: false, null: false
add :meaning, :text
add :category, {:array, :string}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a way to make it array of enum values?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, I think it is possible, I would need to check but I think it can be done. But my doubt here is that we were letting the user add categories from the UI where we are providing the options in a dropdown. So I was thinking about whether we should keep controlling the user input from the UI itself.

We can maintain this enum in the database and then fetch these categories for options to be provided in the UI. But this might be not the very ideal case given that the categories are going to be mostly unchanged, and also doing this would make the frontend make a request every time the form comes up which could be unnecessary given the mentioned reason.

We can also maintain the categories in 2 places, one in the database and the other in the UI, but we would need to sync them in case there is any change in the categories.

For these reasons, I thought to control the categories from the UI only. Please let me know what your thoughts on this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you correctly said, the categories wont be changed often, so it doesnt justify making an API call for them. We'll maintain the category list in two places - backend and frontend. we'll have to remember to update at both places when changes need to be made.
My reason to insist on using enums for this is that it would be more egregious if as an error we save a unrecognized category value. So defining this as an enum would protect against it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, so will make it an array of enums them. Will push the changes here.


timestamps(type: :utc_datetime)
end
end
end
26 changes: 26 additions & 0 deletions uli-community/test/support/fixtures/user_contribution_fixtures.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
defmodule UliCommunity.UserContributionFixtures do
@moduledoc """
This module defines test helpers for creating
entities via the `UliCommunity.UserContribution` context.
"""

@doc """
Generate a crowdsourced_slur.
"""
def crowdsourced_slur_fixture(attrs \\ %{}) do
{:ok, crowdsourced_slur} =
attrs
|> Enum.into(%{
appropriated: true,
appropriation_context: true,
casual: true,
category: ["option1", "option2"],
label: "some label",
level_of_severity: "some level_of_severity",
meaning: "some meaning"
})
|> UliCommunity.UserContribution.create_crowdsourced_slur()

crowdsourced_slur
end
end
Loading
Loading