mkhorasani / Streamlit-Authenticator

A secure authentication module to validate user credentials in a Streamlit application.
Apache License 2.0
1.37k stars 229 forks source link

Streamlit-authenticator conflict with streamlit_modal popups #167

Closed oskoa closed 4 weeks ago

oskoa commented 1 month ago

Let's say I am building an app that allows user to upload PDFs and select one of the uploaded PDFs to be displayed in a popup window (from streamlit_modal). The code snippet below, without the authentication bits, successfully does that: user uploads files, chooses one of the files from the selectbox and the file is rendered in a popup window.

The issue is, after I add authentication to the exact same app, the popup window briefly opens and renders the pdf (for a second) and suddenly closes. Something, that streamlit-authenticator does, closes the popup window. Any help with how to solve this issue would be highly appreciated! Thank you.

st.set_page_config(page_title='PDF Viewer', page_icon = "💬", layout = 'wide', initial_sidebar_state = 'auto')

with open('config.yaml') as file:
    config = yaml.load(file, Loader=SafeLoader)

authenticator = stauth.Authenticate(
    config['credentials'],
    config['cookie']['name'],
    config['cookie']['key'],
    config['cookie']['expiry_days'],
    config['preauthorized']
)

authenticator.login()

if st.session_state["authentication_status"]:
    try:
        authenticator.logout()
    except:
        pass
    st.write(f'Welcome *{st.session_state["name"]}*')

    # Initialize session state
    if "chat_history" not in st.session_state:
        st.session_state["chat_history"] = []
    if "options" not in st.session_state:
        st.session_state["options"]=[]
    if "file_dict" not in st.session_state:
        st.session_state["file_dict"] = None

    with st.chat_message("AI"):
        file_message = "Please Choose Your Files (.pdf):"
        uploaded_files = st.file_uploader(file_message, accept_multiple_files=True)

    # get the uploaded file to be queried
    if uploaded_files is not None:
        text=[]
        st.session_state["uploaded_files"] = uploaded_files

        file_dic = {f.name:f for f in st.session_state["uploaded_files"]}
        st.session_state["file_dic"] = file_dic
        options=["Select a document to continue"]
        options.extend([f.name for f in st.session_state["uploaded_files"]])
        st.session_state["file_dic"].update({options[0]:None})
        st.session_state["options"] = options
        for uploaded_file in uploaded_files:
            # read PDF
            text.append(get_pdf_text(uploaded_file))
            uploaded_file.seek(0,0)

        if text:
            with st.chat_message("AI"):
                chat_message = "To view the PDF, select a document from the side bar"
                st.write(chat_message)
                st.session_state["chat_history"].append(
                {"role": "ai", "content": chat_message},
                )

    modal = Modal(key="Demo Key",title="PDF Document")

    with st.sidebar:
        selected_doc = st.selectbox(
                    label="Uploaded files:",
                    key="selected_doc",
                    options=st.session_state["options"],
                    on_change=doc_display,
                    args=(st.session_state["file_dic"], modal.container())
)
AngelicSage commented 4 weeks ago

You do not have the elif conditions in your code. I think that additional code to display the pdf in a modal might help out I hope this code helps:

import streamlit as st
import streamlit_modal as modal
import streamlit_authenticator as stauth
import yaml
from yaml.loader import SafeLoader

st.set_page_config(page_title='PDF Viewer', page_icon="💬", layout='wide', initial_sidebar_state='auto')

# Load configuration
with open('config.yaml') as file:
    config = yaml.load(file, Loader=SafeLoader)

# Initialize authenticator
authenticator = stauth.Authenticate(
    config['credentials'],
    config['cookie']['name'],
    config['cookie']['key'],
    config['cookie']['expiry_days'],
    config['preauthorized']
)

# Authentication
name, authentication_status, username = authenticator.login('Login', 'main')

if st.session_state["authentication_status"]:
    authenticator.logout()
    st.write(f'Welcome *{st.session_state["name"]}*')
    st.title('Some content')

    # Initialize session state variables
    if "chat_history" not in st.session_state:
        st.session_state["chat_history"] = []
    if "options" not in st.session_state:
        st.session_state["options"] = []
    if "file_dict" not in st.session_state:
        st.session_state["file_dict"] = None

    # File uploader
    file_message = "Please Choose Your Files (.pdf):"
    uploaded_files = st.file_uploader(file_message, accept_multiple_files=True)

    if uploaded_files:
        text = []
        st.session_state["uploaded_files"] = uploaded_files

        file_dict = {f.name: f for f in st.session_state["uploaded_files"]}
        st.session_state["file_dict"] = file_dict
        options = ["Select a document to continue"]
        options.extend([f.name for f in st.session_state["uploaded_files"]])
        st.session_state["options"] = options
        for uploaded_file in uploaded_files:
            # Read PDF
            text.append(get_pdf_text(uploaded_file))
            uploaded_file.seek(0, 0)

        if text:
            chat_message = "To view the PDF, select a document from the sidebar"
            st.chat_message("AI").write(chat_message)
            st.session_state["chat_history"].append({"role": "ai", "content": chat_message})

    # Modal initialization
    pdf_modal = modal.Modal(key="pdf_modal", title="PDF Document")

    # Sidebar selectbox
    with st.sidebar:
        selected_doc = st.selectbox(
            label="Uploaded files:",
            options=st.session_state["options"],
            key="selected_doc"
        )

        # Display PDF in modal
        if selected_doc and selected_doc != "Select a document to continue":
            st.session_state["modal_open"] = True
            with pdf_modal.container():
                st.write(f"Displaying {selected_doc}")
                # Add code to display the PDF content
                st.download_button(
                    label="Download PDF",
                    data=st.session_state["file_dict"][selected_doc],
                    file_name=selected_doc,
                    mime="application/pdf"
                )

    if st.session_state["modal_open"]:
        pdf_modal.open()
elif authentication_status is False:
    st.error("Username/password is incorrect")
elif authentication_status is None:
    st.warning("Please enter your username and password")
oskoa commented 4 weeks ago

Thanks for the response. I ended up using st.experimental_dialog instead of streamlit-modal and that fixed the issue but your points are valid.