Take the 2-minute tour ×
Stack Apps is a question and answer site for apps, scripts, and development with the Stack Exchange API. It's 100% free, no registration required.

Soapi.FindUser - Silverlight Edition

alt text

Configuration:

alt text

About

Soapi.FindUser (silverlight) is provides a means to quickly find any user anywhere is the Stackiverse.

You may search any number of Stack Exchange sites.

A click on an item will open the profile page for the user in question.

License

Soapi.FindUser is free to use and is licensed under GPL V2.

Download

Soapi.FindUser is hosted at http://soapi.info/findusers.aspx

Platform

Any platform/browser that supports Silverlight 3. This includes Windows and MacOS.

A Windows Phone 7 version will be forthcoming.

Contact

Soapi.FindUser was created by code poet.

Code

Soapi.FindUser (silverlight) was written in C# using Visual Studio 2008 for use as a reference implementation using Soapi.CS and the full source is included in the Soapi.CS source repository.

This application is also a demonstration of how to use the common API related controls in Soapi.Contrib.Silverlight. This app uses the UserListViewItem and SiteSelector controls.

share|improve this question
    
Nice! The color of your bronze badges is too close to the color of the gold badges, though. –  Dennis Williamson Aug 10 '10 at 15:32
    
Nice example! One suggestion: I think given the per-site meta behavior of simply mirroring users and their reputation from the respective parent site, you should actually exclude all linked-meta sites by default at least, if not entirely, as they do provide more noise than value for this particular use case, IMHO. (In fact that applies to many real world use cases covering more than one Stack Exchange site, though not all of course.) –  Steffen Opel Aug 10 '10 at 15:32
    
@steffen - i have spent most of my time poking at the bits and miss some of the bytes. thanks for the reminder. While it will not quite double the performance, it will reduce the traffic and as you correctly characterize, the noise. thanks for that. –  Sky Sanders Aug 10 '10 at 15:43
    
@steffen - that was easy. changed _sitesToSearch.Add(new SiteListItem { Enabled = true , Site = site }); to _sitesToSearch.Add(new SiteListItem { Enabled = site.State != SiteState.Linked_Meta, Site = site }); and published. –  Sky Sanders Aug 10 '10 at 15:49
    
@dennis - i eye-droppered the badges for the rgb, I must have gotten an edge on the bronze. Thanks for the feedback. If you have the RGB handy I will drop it right in. –  Sky Sanders Aug 10 '10 at 15:52
    
@code - now that's what I call fast turnaround times :) Much better immediately, thanks! –  Steffen Opel Aug 10 '10 at 15:58
    
Try #CC9966 [ ](google.com) –  Dennis Williamson Aug 10 '10 at 17:44
    
@dennis - thank you. looks much better. –  Sky Sanders Aug 10 '10 at 22:00

2 Answers 2

Uh, I hate to say this, but this does not work on Ubuntu. I have MoonLight installed, but it doesn't show anything in Firefox or Chrome. It displays the loading circle for a few seconds and then displays a blank page.

share|improve this answer
    
I will fire up a VM and check it out. –  Sky Sanders Aug 11 '10 at 0:18
    
Moonlight only works against .net 2.0 code (Silverlight 2). It is not yet compatible with 3.5sp (Silverlight 3). So I guess the supported platforms are Windows and Mac. Sorry. –  Sky Sanders Aug 11 '10 at 0:43

Source Code:

As is the case with any application written using Soapi for api access, the code needed to get the data you want from the API is the least amount of code you have to write and by far the easiest to understand.

Here is the source code for single page of the application. (There are several helper classes like value converters to format dates, but they are not relevant)

//  
//  Project: SOAPI
//  http://soapics.codeplex.com
//  http://stackapps.com/questions/386
//  
//  Copyright 2010, Sky Sanders
//  Licensed under the GPL Version 2 license.
//  http://soapics.codeplex.com/license
//  
//  Date: Aug 08 2010 
//  API ver 1.0 rev 2010.0709.04
//  

#region

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Controls;
using Soapi;
using Soapi.Domain;
using Soapi.Net;
using Soapi.Parameters;

#endregion

namespace FindUser
{
    public partial class MainPage
    {
        #region Constants

        private const string ApiKey = "qgAq_KfDu0KYzlNG-qaTuw";

        #endregion

        #region Fields

        private readonly ApiContext _context;

        private ObservableCollection<SiteListItem> _sitesToSearch;

        #endregion

        #region Constructors

        public MainPage()
        {
            InitializeComponent();
            SearchButton.IsEnabled = false;
            FuqqinBusyIndicator.IsBusy = true;
            FuqqinBusyIndicator.BusyContent = "Initializing";
            RequestCache.Instance.DefaultDuration = new TimeSpan(0, 0, 0, 1);

            // set up an ApiContext

            _context = new ApiContext(ApiKey);
            _context.Initialized += (e, s) => Dispatcher.BeginInvoke(() =>
                {
                    _sitesToSearch = new ObservableCollection<SiteListItem>();
                    _context.Sites.ForEach(site =>
                        {
                            if (site.ApiEndpoint.IndexOf("stackauth") == -1)
                            {
                                // _sitesToSearch.Add(new SiteListItem { Enabled = true, Site = site });
                                // as per steffen's suggestion
                                _sitesToSearch.Add(new SiteListItem 
                                         { 
                                            Enabled = site.State != SiteState.Linked_Meta, 
                                            Site = site 
                                         });
                            }
                        });
                    FuqqinBusyIndicator.IsBusy = false;
                });
            _context.Initialize(true);
        }

        #endregion

        #region Private Methods

        private void EnsureSiteButtonState()
        {
            SearchButton.IsEnabled = SearchTermTextBox.Text.Length > 2 && _sitesToSearch.Any(ss => ss.Enabled);
        }

        private void SearchButtonClick(object sender, RoutedEventArgs e)
        {
            FuqqinBusyIndicator.IsBusy = true;
            FuqqinBusyIndicator.BusyContent = "Spinning up....";

            string searchTerm = SearchTermTextBox.Text;
            var users = new List<User>();
            var sites = _sitesToSearch.Where(s => s.Enabled).Select(s => s.Site).ToList();
            var state = new CounterState { _count = sites.Count };

            ThreadPool.QueueUserWorkItem(o =>
                {
                    // enumerate sites to be searched

                    foreach (var site in sites)
                    {
                        // I tried running each site in parallel but
                        // it just all goes pear shaped and it actually
                        // takes longer.

                        string siteName = site.Name;
                        Dispatcher.BeginInvoke(
                            () =>
                            {
                                FuqqinBusyIndicator.BusyContent =
                                    String.Format("searching {0} ({1} sites remaining)", siteName, state._count);
                            });

                        // Get a list of all users that match the criteria

                        var possibleMatches = site
                            .Users
                            .Filter(searchTerm)
                            .PageCount(-1)
                            .Sort(UserSort.Name).ToList();

                        // And that is it. The rest of the code is related
                        // to presenting the data in an appealing format

                        var gate = new AutoResetEvent(false);

                        foreach (var user in possibleMatches)
                        {
                            users.Add(user);
                        }

                        state._count--;

                        if (state._count == 0)
                        {
                            Dispatcher.BeginInvoke(() =>
                                {
                                    UserListView.ItemsSource = users;
                                    FuqqinBusyIndicator.IsBusy = false;
                                });
                        }
                    }
                });
        }

        private void SearchTermTextBoxTextChanged(object sender, TextChangedEventArgs e)
        {
            EnsureSiteButtonState();
        }

        private void SitesButtonClick(object sender, RoutedEventArgs e)
        {
            var ssw = new SiteSelectorWindow { SitesToSearch = _sitesToSearch };
            ssw.Closed += (s, ev) =>
                {
                    _sitesToSearch = ssw.SitesToSearch;
                    EnsureSiteButtonState();
                };
            ssw.Show();
        }

        private void SortByActivityButtonClick(object sender, RoutedEventArgs e)
        {
            UserListView.ItemsSource = UserListView.ItemsSource.Cast<User>().ToList()
                .OrderBy(u => u.CreationDate)
                .OrderByDescending(u => u.Reputation)
                .OrderBy(u => u.DisplayName)
                .OrderByDescending(u => u.LastAccessDate);
        }

        private void SortByMembershipButtonClick(object sender, RoutedEventArgs e)
        {
            UserListView.ItemsSource = UserListView.ItemsSource.Cast<User>().ToList()
                .OrderByDescending(u => u.LastAccessDate)
                .OrderByDescending(u => u.Reputation)
                .OrderBy(u => u.DisplayName)
                .OrderBy(u => u.CreationDate);
        }

        private void SortByNameButtonClick(object sender, RoutedEventArgs e)
        {
            UserListView.ItemsSource = UserListView.ItemsSource.Cast<User>().ToList()
                .OrderBy(u => u.CreationDate)
                .OrderByDescending(u => u.LastAccessDate)
                .OrderByDescending(u => u.Reputation)
                .OrderBy(u => u.DisplayName);
        }

        private void SortByRepButtonClick(object sender, RoutedEventArgs e)
        {
            UserListView.ItemsSource = UserListView.ItemsSource.Cast<User>().ToList()
                .OrderBy(u => u.CreationDate)
                .OrderByDescending(u => u.LastAccessDate)
                .OrderBy(u => u.DisplayName)
                .OrderByDescending(u => u.Reputation);
        }

        private void UserListViewSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (UserListView.SelectedItem != null)
            {
                var user = (User)UserListView.SelectedItem;
                HtmlPage.Window.Navigate(new Uri(string.Format("{0}users/{1}", user.Site.SiteUrl, user.UserId)),
                                         "__blank");
            }
        }

        #endregion
    }
}
share|improve this answer

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .