{"id":22343523,"url":"https://github.com/datajuggler/blazorchat","last_synced_at":"2025-07-30T01:33:09.827Z","repository":{"id":55374714,"uuid":"269465627","full_name":"DataJuggler/BlazorChat","owner":"DataJuggler","description":"Blazor Chat was originally a sample project of DataJuggler.Blazor.Components, but I decided it will be easier to maintain as its own project.","archived":false,"fork":false,"pushed_at":"2021-11-06T12:02:14.000Z","size":1527,"stargazers_count":7,"open_issues_count":1,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-05T23:41:13.157Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DataJuggler.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-06-04T21:07:05.000Z","updated_at":"2022-04-27T21:14:55.000Z","dependencies_parsed_at":"2022-08-14T22:50:29.717Z","dependency_job_id":null,"html_url":"https://github.com/DataJuggler/BlazorChat","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/DataJuggler/BlazorChat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DataJuggler%2FBlazorChat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DataJuggler%2FBlazorChat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DataJuggler%2FBlazorChat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DataJuggler%2FBlazorChat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DataJuggler","download_url":"https://codeload.github.com/DataJuggler/BlazorChat/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DataJuggler%2FBlazorChat/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267792752,"owners_count":24144931,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-29T02:00:12.549Z","response_time":2574,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-04T08:16:17.862Z","updated_at":"2025-07-30T01:33:05.042Z","avatar_url":"https://github.com/DataJuggler.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Blazor Chat\n\nI took the site down because it wasn't worth paying for it every year.\n\nBlazor Chat was originally a sample project of my Nuget package DataJuggler.Blazor.Components, but I decided it will be easier to maintain in its own project.\n\n\u003cbr\u003e\n\u003cimg src=https://github.com/DataJuggler/SharedRepo/blob/master/Shared/Images/BlazorChat.png\u003e\n\u003cbr\u003e\n\nTo run this sample you will need SQL Server or SQL Server Express.\n\n\u003cb\u003eStep 1:\u003c/b\u003e In SQL Server Management Studio, create a database named BlazorChat.\n\n\u003cb\u003eStep 2:\u003c/b\u003e Execute the SQL File located in the SQLScripts folder of this repo. This will create the Tables and Stored Procedures\nused by this project. If you want to learn how to create your own stored procedure powered data tiers, clone my project DataTier.Net:\nhttps://github.com/DataJuggler/DataTier.Net \n\nOr here is a video on it, although I am overdue to make a new one.\n\nhttps://youtu.be/sowTLLeAfm8\n\nPlease subscribe to my YouTube channel.\n\n\u003cb\u003eStep 3:\u003c/b\u003e Create a System Environment Variable with the following values:\n\nTip: To create a System Environment Variable, click on the Search Icon in Windows 10, and start typing 'Edit System Environment Variables' and make sure to use the System settings at the bottom, not the User Settings at the top:\n\nIn the end my setting looks like this, although my actual Server name is hidden.\n\n\u003cbr\u003e\n\u003cimg src=https://github.com/DataJuggler/SharedRepo/blob/master/Shared/Images/EditSystemEnivonmentVariables.png\u003e\n\u003cbr\u003e\n\nName: BlazorChat\n\nValue: Paste in the connection string to your BlazorChat database created in step 1. \n\nMy connection to SQL Server Express looks like this using Windows Authentication\n\nData Source=[ServerName]\\SQLExpress;Initial Catalog=BlazorChat;Integrated Security=True\n\nReplace ServerName with the name of your SQL Server Instance. The easiest way to get your server name is login\nto SQL Server Management Studio, and copy the Server Name it tries to log you in with, if you have it saved.\n\nMy open source project DataTier.Net mentioned in Step 2 comes with a Connection String Builder app, located in the Tools folder:\n\n\u003cbr\u003e\n\u003cimg src=https://github.com/DataJuggler/DataTier.Net/blob/master/DataTier.Net/Class%20Room/Documents/Build%20Connection%20String.png\u003e\nThe image here shows the database for DataTier.Net. Change the Server Name to your Server name and set the database name to BlazorChat.\u003cbr\u003e\u003cbr\u003e\n\u003cbr\u003e\n\u003cb\u003eStep 4:\u003c/b\u003e Run the App. I use Chrome as my main debugging app for Visual Studio, then I launch an Edge\nand a Fire Fox instance if I want 3 users to test on my local machine.\u003cbr\u003e\n\n\u003cb\u003eKnown issues:\u003c/b\u003e\n\n\u003cb\u003e1. Periodic Dispose\u003c/b\u003e\u003cbr\u003e\nI added a dispose method, and in theory when the user closes their browser, this should unsubscribe.\nA few days ago I had a power failure during a storm, and I am fairly sure during a hard close no code is going to\nexecute without power to the browser, so a \"Remove If Idle\" method is probably needed. Where each user\nwould be pinged periodically and if there ins't a response they are removed from the list of Subscribers.\u003cbr\u003e\n\n\u003cb\u003e2. The Remember Me checkbox position moves sometimes after a new user signs up\u003c/b\u003e\u003cbr\u003e\nThe position is fine for a returning user, so I hate to dabble with it, but please if anyone feels capable of solving the why this happens, I would like to know because I have the same bug on my site https://pixeldatabase.net, because most of the code for this app\ncame from PixelDatabase.Net.\n\n\u003cb\u003e3. A couple of times a login failed and I was not shown a message.\u003c/b\u003e\u003cbr\u003e\nI believe this issue is now fixed, but only time will tell.\n\n\u003cbr\u003e\n\u003cb\u003eGeek Zone\u003c/b\u003e\n\u003cimg src=https://github.com/DataJuggler/SharedRepo/blob/master/Shared/Images/Codey.png\u003e\n\u003cbr\u003e\n\u003cb\u003eUnder The Hood Areas Of Interest areas\u003c/b\u003e\u003cbr\u003e\u003cbr\u003e\n\u003cb\u003e1. Index.razor and Index.razor.cs.\u003c/b\u003e\u003cbr\u003e\nThis is the main page of the app and the method SetupScreen(ScreenTypeEnum screenType) is called by the child components Login.razor or Join.razor.\n\nScreenType is used to either show the Login, Join or Chat components.\n\nScreenType enum has 3 options:\n\n    public enum ScreenTypeEnum : int\n    {\n        Main = 0,\n        Join = 1,\n        Login = 2\n    }\n\n\u003cb\u003eParent / Child Communication\u003c/b\u003e\u003cbr\u003e\n\n    @if (ScreenType == ScreenTypeEnum.Join)\n    { \n        \u003cJoin Parent=this\u003e\u003c/Join\u003e\n    }\n    else if (ScreenType == ScreenTypeEnum.Login)\n    {\n        \u003cLogin Parent=this\u003e\u003c/Login\u003e\n    }\n    else\n    {\n        \u003cChat Parent=this\u003e\u003c/Chat\u003e\n        @if (TextHelper.Exists(Message))\n        {  \n            \u003cdiv class=\"message\"\u003e\n                @Message\n            \u003c/div\u003e\n        }\n    } \n    \nNotice the Login, Join and Chat components all have Parent=this.\n\nThis is possible because the Index page implements IBlazorComponentParent and the child components are IBlazorComponent objects.\n\nThe setter for each of the child components, call the Register method of the Index page, and as a result the child objects have a property Parent and ParentIndexPage, which is just the IBlazorComponentParent cast as an Index (page) object. \n\nAlso, the Index page now has a reference to the Login, Join or Chat components. I come from a Windows Forms background originally, and being able to refer to the child objects like this makes me feel right at home.\n\nI wrote a block post in January talking about parent child communication if you would like to read more:\u003cbr\u003e\nhttps://datajugglerblazor.blogspot.com/2020/01/how-to-use-interfaces-to-communicate.html\n\nOr if you would like to look at the source code is available here:\nDataJuggler.Blazor.Components:\u003cbr\u003e\nhttps://github.com/DataJuggler/DataJuggler.Blazor.Components\n\nFirst If a user is not logged in, they are shown this message:\u003cbr\u003e\n\u003cimg src=https://github.com/DataJuggler/BlazorChat/blob/master/wwwroot/Images/SignUpToChat.png\u003e\u003cbr\u003e\u003cbr\u003e\n\n\n\nOnce you have a LoggedInUser, the user must click the Connect button, which calls the RegisterWithServer method:\n\u003cbr\u003e\n\u003cimg src=https://github.com/DataJuggler/SharedRepo/blob/master/Shared/Images/BlazorChatHouseRules.png\u003e\u003cbr\u003e\n\n\u003cb\u003e2. DataJuggler.Blazor.Components\u003c/b\u003e\u003cbr\u003e\nThis is my Nuget package I created that led to this BlazorChat project being created.\n\n    \u003cb\u003eValidationComponent\u003c/b\u003e\u003cbr\u003e\n    The ValidationComponent has properties for Required, and also a property can be set for 'IsUniqueCallbackRequired'.\n    \n    \u003cb\u003eIsUniqueCallbackRequired\u003c/b\u003eIf this parameter is set to true:\n    \n    \u003cValidationComponent Name=\"UserNameComponent\" Parent=this\n        Caption=\"User Name\" IsRequired=\"true\" MinimumLength=\"5\"\n        MaximumLength=\"20\" IsUniqueCallbackRequired=\"true\"             \n        ValidationMessage=\"User Name Must Be Between 5 And 20 Characters\"\u003e\n    \u003c/ValidationComponent\u003e\n    \nWhen the Validate method is called, if IsUniqueCallbackRequired = true, then the component makes a call to its parent and the parent executges a method a database look up to see if a UserName or EmailAddress already exists.\n\n\u003cbr\u003e\n\u003cimg src=https://github.com/DataJuggler/SharedRepo/blob/master/Shared/Images/ValidationComponent.png\u003e\nThe User Name field had passed validation, including the call to the database to check uniqueness, but the email address is not filled in.\n\u003cbr\u003e\n\n    \u003cb\u003eProgress Bar\u003c/b\u003e\u003cbr\u003e\n    It took me 3 attempts to get a progress bar I really like, and I owe the credit to the CSS Percentage Circle:     \n    \n    CSS Percentage Circle\n    Author: Andre Firchow\n    \n    http://circle.firchow.net/\n\n    And this app links to this file included with DataJuggler.Blazor.Components Nuget package:\n\n    \u003clink href=\"~/_content/DataJuggler.Blazor.Components/css/circle.css\" rel=\"stylesheet\" /\u003e\n    \n    \u003cb\u003eSprite Component\u003c/b\u003e\u003cbr\u003e\n    I am not using the Sprite component to display an image like it was designed for, but I created a property called \u003cb\u003eInvisibleSprite\u003c/b\u003e on the Join and the Login components that I use for the Timer. I set the Interval of the Sprite like this:\n    \n     \u003cSprite Subscriber=this Visible=\"true\" Interval=\"50\" Position=\"fixed\" \n         XPosition=\"-200\" YPosition=\"-200\" Width=\"100\" Height=\"100\"\u003e\n     \u003c/Sprite\u003e\n     \n By setting the position off screen, the Sprite still registers, but the users doesn't see anything. The Interval of 50 means 20 times per seconds the component should be called. I use the Refresh method of the progress bar to increment the progress, because I really don't know how long it is going to take for the call to ValidateUser to execute.\n \n \u003cb\u003e3. Subscriber Service\u003c/b\u003e\u003cbr\u003e\n The SubscriberService is a class that keeps track of the messages. This class is a Singleton available to all users thanks to this line in Startup.cs:\n \n     services.AddSingleton\u003cSubscriberService\u003e();\n     \n And this service is injected into the Chat component like this:\n \n     @inject Services.SubscriberService SubscriberService\n     \n \u003cb\u003e4. SpeechBubble Component\u003c/b\u003e\u003cbr\u003e\n This component is used to display the messages typed by a user. One cool think I just learned how to use was HtmlSanitizer.\n \n I am using the Nuget package HtmlSanitizer and I found this class that uses it from one of their examples:\n \n     \u003cb\u003eclass MarkupStringExtension\u003c/b\u003e\u003cbr\u003e\n     \nThis class allows a user to type HTML, and it removes harmful links and formats a string so you type \u003cb\u003eBold a word or phrase\u003c/b\u003e like this. You can also type in a link, and I wrote some code to convert the link to an anchor tag and set the href so it is clickable.\n\nThe call to use it is like this in the Razor markup:\n\n    \u003cdiv class=\"@TextStyle\"\u003e\n        \u003cspan\u003e@(((MarkupString)HtmlText).Sanitize())\u003c/span\u003e\n    \u003c/div\u003e\n    \nI have two properites on the SpeechBubble. Text and HtmlText. It wasn't really needed, but when you set the value for Text, the value for HtmlText is set to Text, so this keeps them separated (kind of sounds like a song by OffSpring).\n\nFor now, I randomly choose which bubble color to show for each message. It probably should let a user pick a color, or even show an avatar next to a message post, but I have to start with baby steps.\n\nTo do that I use my Nuget package: DataJuggler.RandomShuffler.Core\n\n\n\u003cb\u003e5. RandomShuffler\u003c/b\u003e\u003cbr\u003e\n\nThis was one of my earliest Nuget packages and I still use it because it provides for even distribution compared to true random numbers which could take thousands or millions of years to evenly distribute randomness. I use this compnent to pick which bubble color to show.\n\nThere are 6 Bubble colors available, and the images can be found in the wwwroot/Images/Bubbles folder.\n\nusing DataJuggler.RandomShuffler.Core;\n\n    // Create a Shuffler\n    Shuffler = new RandomShuffler(1, 6, 1, 10);\n    \nWhat this does is create a shuffler that fills in the items from min to max (1 to 6) and creates only 1 set of numbers, and shuffles 10 times. The Shuffler automatically recreates itself if you pull all the numbers in the PullNextItem method.\n\nI prefer this type of shuffling, as it truly shuffles and pulls items. There are also methods for Cards and I have a program called Card Counter to teach you how to learn to count cards in Black Jack, but that project is coming this summer.\n\nhttps://github.com/DataJuggler/RandomShuffler.Core\nNuget: uDataJuggler.RandomShuffler.Core;\n\nChat.razor has a Send message, and here I pick a random bubble color:\n\nmessage.BubbleColor = (BubbleColorEnum) Shuffler.PullNextItem();\n\n\u003cb\u003eBubbleColorEnum\u003c/b\u003e\u003cbr\u003e\n\n\n    public enum BubbleColorEnum : int\n    {\n        NotSet = 0,\n        BlueGreen = 1,        \n        BlueRed = 2,\n        GreenBlue = 3,\n        GreenRed = 4,\n        RedBlue = 5,\n        RedGreen = 6,\n    }\n\nSo the integer value pulled above converts to the image shown, and the url for I\n\nThe SpeechBubble uses BlazorStyled, where I have a CSS property set like this:\n\n@using BlazorStyled\n\n\u003cStyled @bind-Classname=\"@BubbleStyle\"\u003e\n    background-image: url('@ImageUrl');\n    background-repeat: no-repeat;\n    background-size: 100% 100%;\n    position: relative;\n    left: 0%;\n    top: 0vh;\n    width: 84%;\n    height: auto;\n    margin-bottom: 1.2vh;\n\u003c/Styled\u003e\n\nAnd the @ImageUrl propperty is set by the Chat component when it sets the BubbleColorEnum of the message:\n\nHere in the SubscriberMessage class, the BubbleColor property looks like this and sets the ImageUrl whenever the BubbleColor is set.\n\n    public BubbleColorEnum BubbleColor\n    {\n        get { return bubbleColor; }\n        set \n        {\n            // set the bubbleColor\n            bubbleColor = value;\n\n            // Set the ImageUrl\n            SetImageUrl();\n        }\n    }\n    \n    \u003cb\u003e6. Listen method\u003c/b\u003e\u003cbr\u003e\n    The Listen method is the delegate callback that a SubscriberCallback sets when it registers with the Server.\n    \n        public void Listen(SubscriberMessage message)\n        {\n            // if the message exists\n            if (NullHelper.Exists(message))\n            {\n                // if the message is a SystemMessage\n                if (message.IsSystemMessage)\n                {   \n                    // Get the Names again\n                    this.Names = SubscriberService.GetSubscriberNames();\n\n                    // Update the UI\n                    Refresh();\n                }\n                else\n                {  \n                    // Get the Messages\n                    this.Messages = SubscriberService.GetBroadcastMessages(this.Id);\n\n                    // Update the UI\n                    Refresh();                   \n                }\n            }\n        }\n    \n        For now I have it hard coded to get the last 10 messages, but with more users I am thinking that should be a little higher.\n   \n\n\n\u003cb\u003eState Of the App\u003c/b\u003e\nI am a little past prototype at this stage, but a large amount of testing has not been done.\nI am sure there are probably plenty of bugs once others start using this. \nI will publish it to my server soon with a live sample.\u003cbr\u003e\u003cbr\u003e\n\n\u003cb\u003eSuggestions for version 2.0:\u003c/b\u003e\n1. I added links and you can post HTML if you know it, but I think the next features are going to be post images and video\nwith maybe some like, Up / Down votes or Stars or something to rate posts.\n2. The current version only shows the last 10 messages. I didn't do anything involving private messages and priority,\nI just get the last 10 messages and show them if they are public, or to or from for private messages.\n3. I might save the messages to the database if I ever wanted to expand it to be not just a demo.\n4. I stubbed out a Blocked user list for users, but didn't do anything with it. Perhaps a 'Vote Off The Island' if\na user is rude, abusive or a spammer.\n5. I will probably add a way to rate posts\n6. Some type of Admin feature so users can be warned first and then booted for abusive behavior.\n\nMainly this was a demo. I strated to use GRPC, but every sample I tried with 2 way communication was overkill\nconsidering all I needed was to talk to a client that is on the same server as another. \n\nThis was all I needed to send messages to all users. \n\nLet me know your thoughts, comments, suggestions etc. by creating an issue here or in the comments on the BlazorChat\nvideo, which I am making now and my time machine is broken so I have to post it first.\n\nTo Do: Publish url to cool video.\n\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatajuggler%2Fblazorchat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdatajuggler%2Fblazorchat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatajuggler%2Fblazorchat/lists"}